chore: apply requested changes
This commit is contained in:
@@ -2,16 +2,12 @@
|
||||
"name": "@tm/ai-sdk-provider-grok-cli",
|
||||
"private": true,
|
||||
"description": "AI SDK provider for Grok CLI integration",
|
||||
"keywords": ["ai", "grok", "x.ai", "cli", "language-model", "provider"],
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"types": "./src/index.ts",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"files": ["dist/**/*", "README.md"],
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
@@ -28,14 +24,11 @@
|
||||
"typescript": "^5.9.2",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ai-sdk/provider": "^2.0.0",
|
||||
"@ai-sdk/provider-utils": "^3.0.10",
|
||||
"jsonc-parser": "^3.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"keywords": ["ai", "grok", "x.ai", "cli", "language-model", "provider"],
|
||||
"files": ["dist/**/*", "README.md"],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ export class GrokCliLanguageModel implements LanguageModelV2 {
|
||||
*/
|
||||
private async executeGrokCli(
|
||||
args: string[],
|
||||
options: { timeout?: number } = {}
|
||||
options: { timeout?: number; apiKey?: string } = {}
|
||||
): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
||||
// Default timeout based on model type
|
||||
let defaultTimeout = 120000; // 2 minutes default
|
||||
@@ -123,7 +123,11 @@ export class GrokCliLanguageModel implements LanguageModelV2 {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn('grok', args, {
|
||||
stdio: 'pipe',
|
||||
cwd: this.settings.workingDirectory || process.cwd()
|
||||
cwd: this.settings.workingDirectory || process.cwd(),
|
||||
env:
|
||||
options.apiKey === undefined
|
||||
? process.env
|
||||
: { ...process.env, GROK_CLI_API_KEY: options.apiKey }
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
@@ -295,7 +299,7 @@ export class GrokCliLanguageModel implements LanguageModelV2 {
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.executeGrokCli(args);
|
||||
const result = await this.executeGrokCli(args, { apiKey });
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
// Handle authentication errors
|
||||
@@ -323,11 +327,14 @@ export class GrokCliLanguageModel implements LanguageModelV2 {
|
||||
let text = response.text || '';
|
||||
|
||||
// Extract JSON if in object-json mode
|
||||
if (
|
||||
'mode' in options &&
|
||||
(options as any).mode?.type === 'object-json' &&
|
||||
text
|
||||
) {
|
||||
const isObjectJson = (
|
||||
o: unknown
|
||||
): o is { mode: { type: 'object-json' } } =>
|
||||
!!o &&
|
||||
typeof o === 'object' &&
|
||||
'mode' in o &&
|
||||
(o as any).mode?.type === 'object-json';
|
||||
if (isObjectJson(options) && text) {
|
||||
text = extractJson(text);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,49 +9,49 @@ describe('extractJson', () => {
|
||||
it('should extract JSON from markdown code blocks', () => {
|
||||
const text = '```json\n{"name": "test", "value": 42}\n```';
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('{"name": "test", "value": 42}');
|
||||
expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
|
||||
});
|
||||
|
||||
it('should extract JSON from generic code blocks', () => {
|
||||
const text = '```\n{"name": "test", "value": 42}\n```';
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('{"name": "test", "value": 42}');
|
||||
expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
|
||||
});
|
||||
|
||||
it('should remove JavaScript variable declarations', () => {
|
||||
const text = 'const result = {"name": "test", "value": 42};';
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('{"name": "test", "value": 42}');
|
||||
expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
|
||||
});
|
||||
|
||||
it('should handle let variable declarations', () => {
|
||||
const text = 'let data = {"name": "test", "value": 42};';
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('{"name": "test", "value": 42}');
|
||||
expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
|
||||
});
|
||||
|
||||
it('should handle var variable declarations', () => {
|
||||
const text = 'var config = {"name": "test", "value": 42};';
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('{"name": "test", "value": 42}');
|
||||
expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
|
||||
});
|
||||
|
||||
it('should extract JSON arrays', () => {
|
||||
const text = '[{"name": "test1"}, {"name": "test2"}]';
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('[{"name": "test1"}, {"name": "test2"}]');
|
||||
expect(JSON.parse(result)).toEqual([{ name: 'test1' }, { name: 'test2' }]);
|
||||
});
|
||||
|
||||
it('should convert JavaScript object literals to JSON', () => {
|
||||
const text = "{name: 'test', value: 42}";
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('{"name": "test", "value": 42}');
|
||||
expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
|
||||
});
|
||||
|
||||
it('should return valid JSON as-is', () => {
|
||||
it('should return valid JSON (canonical formatting)', () => {
|
||||
const text = '{"name": "test", "value": 42}';
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('{"name": "test", "value": 42}');
|
||||
expect(JSON.parse(result)).toEqual({ name: 'test', value: 42 });
|
||||
});
|
||||
|
||||
it('should return original text when JSON parsing fails completely', () => {
|
||||
@@ -76,6 +76,6 @@ describe('extractJson', () => {
|
||||
it('should handle mixed quotes in object literals', () => {
|
||||
const text = `{name: "test", value: 'mixed quotes'}`;
|
||||
const result = extractJson(text);
|
||||
expect(result).toBe('{"name": "test", "value": "mixed quotes"}');
|
||||
expect(JSON.parse(result)).toEqual({ name: 'test', value: 'mixed quotes' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,6 +121,19 @@ describe('createPromptFromMessages', () => {
|
||||
|
||||
expect(result).toBe('custom: Custom message');
|
||||
});
|
||||
|
||||
it('should trim whitespace from message content', () => {
|
||||
const messages = [
|
||||
{ role: 'user', content: ' Hello with spaces ' },
|
||||
{ role: 'assistant', content: '\n\nResponse with newlines\n\n' }
|
||||
];
|
||||
|
||||
const result = createPromptFromMessages(messages);
|
||||
|
||||
expect(result).toBe(
|
||||
'User: Hello with spaces\n\nAssistant: Response with newlines'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('escapeShellArg', () => {
|
||||
|
||||
@@ -81,7 +81,7 @@ export function convertFromGrokCliResponse(responseText: string): {
|
||||
|
||||
// Find the last assistant message
|
||||
const assistantMessage = messages
|
||||
.filter((msg: any) => msg.role === 'assistant')
|
||||
.filter((msg) => msg.role === 'assistant')
|
||||
.pop();
|
||||
|
||||
if (assistantMessage && assistantMessage.content) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
@@ -24,7 +24,7 @@
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "NodeNext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"types": ["node"],
|
||||
"resolveJsonModule": true,
|
||||
|
||||
Reference in New Issue
Block a user