@mmnto/cli 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/commands/drift.d.ts +2 -0
  2. package/dist/commands/drift.d.ts.map +1 -0
  3. package/dist/commands/drift.js +45 -0
  4. package/dist/commands/drift.js.map +1 -0
  5. package/dist/commands/drift.test.d.ts +2 -0
  6. package/dist/commands/drift.test.d.ts.map +1 -0
  7. package/dist/commands/drift.test.js +92 -0
  8. package/dist/commands/drift.test.js.map +1 -0
  9. package/dist/commands/extract.d.ts +22 -1
  10. package/dist/commands/extract.d.ts.map +1 -1
  11. package/dist/commands/extract.js +119 -28
  12. package/dist/commands/extract.js.map +1 -1
  13. package/dist/commands/extract.test.js +216 -2
  14. package/dist/commands/extract.test.js.map +1 -1
  15. package/dist/commands/handoff.d.ts +2 -0
  16. package/dist/commands/handoff.d.ts.map +1 -1
  17. package/dist/commands/handoff.js +61 -2
  18. package/dist/commands/handoff.js.map +1 -1
  19. package/dist/commands/handoff.test.js +33 -1
  20. package/dist/commands/handoff.test.js.map +1 -1
  21. package/dist/commands/shield-learn.test.d.ts +2 -0
  22. package/dist/commands/shield-learn.test.d.ts.map +1 -0
  23. package/dist/commands/shield-learn.test.js +123 -0
  24. package/dist/commands/shield-learn.test.js.map +1 -0
  25. package/dist/commands/shield.d.ts +5 -0
  26. package/dist/commands/shield.d.ts.map +1 -1
  27. package/dist/commands/shield.js +154 -6
  28. package/dist/commands/shield.js.map +1 -1
  29. package/dist/commands/shield.test.js +28 -1
  30. package/dist/commands/shield.test.js.map +1 -1
  31. package/dist/index.js +15 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/orchestrators/conformance.test.js +25 -1
  34. package/dist/orchestrators/conformance.test.js.map +1 -1
  35. package/dist/orchestrators/ollama-orchestrator.d.ts +13 -0
  36. package/dist/orchestrators/ollama-orchestrator.d.ts.map +1 -0
  37. package/dist/orchestrators/ollama-orchestrator.js +91 -0
  38. package/dist/orchestrators/ollama-orchestrator.js.map +1 -0
  39. package/dist/orchestrators/ollama-orchestrator.test.d.ts +2 -0
  40. package/dist/orchestrators/ollama-orchestrator.test.d.ts.map +1 -0
  41. package/dist/orchestrators/ollama-orchestrator.test.js +131 -0
  42. package/dist/orchestrators/ollama-orchestrator.test.js.map +1 -0
  43. package/dist/orchestrators/openai-orchestrator.d.ts +12 -0
  44. package/dist/orchestrators/openai-orchestrator.d.ts.map +1 -0
  45. package/dist/orchestrators/openai-orchestrator.js +70 -0
  46. package/dist/orchestrators/openai-orchestrator.js.map +1 -0
  47. package/dist/orchestrators/openai-orchestrator.test.d.ts +2 -0
  48. package/dist/orchestrators/openai-orchestrator.test.d.ts.map +1 -0
  49. package/dist/orchestrators/openai-orchestrator.test.js +114 -0
  50. package/dist/orchestrators/openai-orchestrator.test.js.map +1 -0
  51. package/dist/orchestrators/orchestrator.d.ts.map +1 -1
  52. package/dist/orchestrators/orchestrator.js +15 -1
  53. package/dist/orchestrators/orchestrator.js.map +1 -1
  54. package/dist/orchestrators/orchestrator.test.js +35 -0
  55. package/dist/orchestrators/orchestrator.test.js.map +1 -1
  56. package/package.json +8 -3
@@ -0,0 +1,91 @@
1
+ import { z } from 'zod';
2
+ import { log } from '../ui.js';
3
+ import { isQuotaError } from './orchestrator.js';
4
+ // ─── Constants ───────────────────────────────────────
5
+ const DEFAULT_BASE_URL = 'http://localhost:11434';
6
+ // ─── Response schema ────────────────────────────────
7
+ const OllamaChatResponseSchema = z.object({
8
+ message: z.object({ content: z.string().optional() }).optional(),
9
+ prompt_eval_count: z.number().optional(),
10
+ eval_count: z.number().optional(),
11
+ done: z.boolean().optional(),
12
+ done_reason: z.string().optional(),
13
+ });
14
+ // ─── Native Ollama orchestrator ─────────────────────
15
+ /**
16
+ * Invoke Ollama's native /api/chat endpoint directly via fetch.
17
+ * Unlike the OpenAI-compatible adapter, this supports passing `num_ctx`
18
+ * to dynamically control context length (and VRAM usage).
19
+ *
20
+ * @see https://github.com/mmnto-ai/totem/issues/298
21
+ */
22
+ export async function invokeOllamaOrchestrator(opts) {
23
+ const { prompt, model, tag, baseUrl, numCtx } = opts;
24
+ // Normalize base URL (strip trailing slash)
25
+ const base = (baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
26
+ const url = `${base}/api/chat`;
27
+ const ctxLabel = numCtx ? ` (num_ctx: ${numCtx})` : '';
28
+ log.info(tag, `Invoking Ollama at ${base}${ctxLabel} (this may take 15-60 seconds)...`);
29
+ const startMs = Date.now();
30
+ const body = {
31
+ model,
32
+ stream: false,
33
+ messages: [{ role: 'user', content: prompt }],
34
+ };
35
+ // Only inject num_ctx if explicitly configured
36
+ if (numCtx) {
37
+ body.options = { num_ctx: numCtx };
38
+ }
39
+ let response;
40
+ try {
41
+ response = await fetch(url, {
42
+ method: 'POST',
43
+ headers: { 'Content-Type': 'application/json' },
44
+ body: JSON.stringify(body),
45
+ });
46
+ }
47
+ catch (err) {
48
+ const msg = err instanceof Error ? err.message : String(err);
49
+ throw new Error(`[Totem Error] Cannot connect to Ollama at ${base}.\n` +
50
+ `Is Ollama running? Start it with: ollama serve\n` +
51
+ `Details: ${msg}`);
52
+ }
53
+ if (!response.ok) {
54
+ const errorBody = await response.text();
55
+ if (isQuotaError(Object.assign(new Error(errorBody), { status: response.status }))) {
56
+ const err = new Error(`Ollama rate limit: ${errorBody}`);
57
+ err.name = 'QuotaError';
58
+ throw err;
59
+ }
60
+ // 500 errors from Ollama are often VRAM/context exhaustion
61
+ if (response.status >= 500) {
62
+ throw new Error(`[Totem Error] Ollama server error (${response.status}): ${errorBody}\n` +
63
+ (numCtx
64
+ ? `Try lowering numCtx (currently ${numCtx}) in your orchestrator config.`
65
+ : `Try setting a smaller numCtx in your orchestrator config to limit VRAM usage.`));
66
+ }
67
+ throw new Error(`[Totem Error] Ollama API error (${response.status}): ${errorBody}`);
68
+ }
69
+ let raw;
70
+ try {
71
+ raw = await response.json();
72
+ }
73
+ catch {
74
+ throw new Error(`[Totem Error] Ollama returned invalid JSON. Is the model loaded?`);
75
+ }
76
+ const parsed = OllamaChatResponseSchema.safeParse(raw);
77
+ if (!parsed.success) {
78
+ throw new Error(`[Totem Error] Unexpected response from Ollama API.\n` +
79
+ `Validation: ${parsed.error.issues.map((i) => i.message).join(', ')}`);
80
+ }
81
+ const data = parsed.data;
82
+ const durationMs = Date.now() - startMs;
83
+ return {
84
+ content: data.message?.content ?? '',
85
+ inputTokens: data.prompt_eval_count ?? null,
86
+ outputTokens: data.eval_count ?? null,
87
+ durationMs,
88
+ finishReason: data.done_reason ?? (data.done ? 'stop' : undefined),
89
+ };
90
+ }
91
+ //# sourceMappingURL=ollama-orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama-orchestrator.js","sourceRoot":"","sources":["../../src/orchestrators/ollama-orchestrator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ollama-orchestrator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama-orchestrator.test.d.ts","sourceRoot":"","sources":["../../src/orchestrators/ollama-orchestrator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,131 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { invokeOllamaOrchestrator } from './ollama-orchestrator.js';
3
+ // ─── Mocks ──────────────────────────────────────────
4
+ vi.mock('../ui.js', () => ({
5
+ log: { info: vi.fn(), success: vi.fn(), warn: vi.fn(), error: vi.fn(), dim: vi.fn() },
6
+ }));
7
+ const mockFetch = vi.fn();
8
+ vi.stubGlobal('fetch', mockFetch);
9
+ afterEach(() => {
10
+ mockFetch.mockReset();
11
+ });
12
+ // ─── Tests ──────────────────────────────────────────
13
+ describe('invokeOllamaOrchestrator', () => {
14
+ const baseOpts = {
15
+ prompt: 'test prompt',
16
+ model: 'gemma2:27b',
17
+ cwd: '.',
18
+ tag: 'Test',
19
+ totemDir: '.totem',
20
+ };
21
+ const okResponse = (body) => new Response(JSON.stringify(body), {
22
+ status: 200,
23
+ headers: { 'Content-Type': 'application/json' },
24
+ });
25
+ it('returns structured result from Ollama', async () => {
26
+ mockFetch.mockResolvedValueOnce(okResponse({
27
+ message: { content: 'Hello from Ollama' },
28
+ prompt_eval_count: 150,
29
+ eval_count: 50,
30
+ done: true,
31
+ done_reason: 'stop',
32
+ }));
33
+ const result = await invokeOllamaOrchestrator(baseOpts);
34
+ expect(result.content).toBe('Hello from Ollama');
35
+ expect(result.inputTokens).toBe(150);
36
+ expect(result.outputTokens).toBe(50);
37
+ expect(result.finishReason).toBe('stop');
38
+ expect(result.durationMs).toBeGreaterThanOrEqual(0);
39
+ });
40
+ it('passes num_ctx in options when configured', async () => {
41
+ mockFetch.mockResolvedValueOnce(okResponse({ message: { content: 'ok' }, done: true }));
42
+ await invokeOllamaOrchestrator({ ...baseOpts, numCtx: 8192 });
43
+ const [, fetchOpts] = mockFetch.mock.calls[0];
44
+ const body = JSON.parse(fetchOpts.body);
45
+ expect(body.options).toEqual({ num_ctx: 8192 });
46
+ });
47
+ it('omits options.num_ctx when not configured', async () => {
48
+ mockFetch.mockResolvedValueOnce(okResponse({ message: { content: 'ok' }, done: true }));
49
+ await invokeOllamaOrchestrator(baseOpts);
50
+ const [, fetchOpts] = mockFetch.mock.calls[0];
51
+ const body = JSON.parse(fetchOpts.body);
52
+ expect(body.options).toBeUndefined();
53
+ });
54
+ it('defaults baseUrl to localhost:11434', async () => {
55
+ mockFetch.mockResolvedValueOnce(okResponse({ message: { content: 'ok' }, done: true }));
56
+ await invokeOllamaOrchestrator(baseOpts);
57
+ const [url] = mockFetch.mock.calls[0];
58
+ expect(url).toBe('http://localhost:11434/api/chat');
59
+ });
60
+ it('uses custom baseUrl and strips trailing slash', async () => {
61
+ mockFetch.mockResolvedValueOnce(okResponse({ message: { content: 'ok' }, done: true }));
62
+ await invokeOllamaOrchestrator({ ...baseOpts, baseUrl: 'http://myserver:11434/' });
63
+ const [url] = mockFetch.mock.calls[0];
64
+ expect(url).toBe('http://myserver:11434/api/chat');
65
+ });
66
+ it('sends model and prompt in correct Ollama format', async () => {
67
+ mockFetch.mockResolvedValueOnce(okResponse({ message: { content: 'ok' }, done: true }));
68
+ await invokeOllamaOrchestrator(baseOpts);
69
+ const [, fetchOpts] = mockFetch.mock.calls[0];
70
+ const body = JSON.parse(fetchOpts.body);
71
+ expect(body.model).toBe('gemma2:27b');
72
+ expect(body.stream).toBe(false);
73
+ expect(body.messages).toEqual([{ role: 'user', content: 'test prompt' }]);
74
+ });
75
+ it('requests non-streaming response', async () => {
76
+ mockFetch.mockResolvedValueOnce(okResponse({ message: { content: 'ok' }, done: true }));
77
+ await invokeOllamaOrchestrator(baseOpts);
78
+ const [, fetchOpts] = mockFetch.mock.calls[0];
79
+ const body = JSON.parse(fetchOpts.body);
80
+ expect(body.stream).toBe(false);
81
+ });
82
+ it('throws connection error when Ollama is unreachable', async () => {
83
+ mockFetch.mockRejectedValueOnce(new Error('ECONNREFUSED'));
84
+ await expect(invokeOllamaOrchestrator(baseOpts)).rejects.toThrow('Cannot connect to Ollama');
85
+ });
86
+ it('suggests ollama serve in connection error', async () => {
87
+ mockFetch.mockRejectedValueOnce(new Error('ECONNREFUSED'));
88
+ await expect(invokeOllamaOrchestrator(baseOpts)).rejects.toThrow('ollama serve');
89
+ });
90
+ it('throws VRAM-friendly error on 500 with numCtx', async () => {
91
+ mockFetch.mockResolvedValueOnce(new Response('out of memory', { status: 500 }));
92
+ await expect(invokeOllamaOrchestrator({ ...baseOpts, numCtx: 32768 })).rejects.toThrow('lowering numCtx');
93
+ });
94
+ it('throws QuotaError on 429 responses', async () => {
95
+ mockFetch.mockResolvedValueOnce(new Response('too many requests', { status: 429 }));
96
+ await expect(invokeOllamaOrchestrator(baseOpts)).rejects.toSatisfy((err) => {
97
+ return err.name === 'QuotaError' && err.message.includes('Ollama rate limit');
98
+ });
99
+ });
100
+ it('throws VRAM-friendly error on 500 without numCtx', async () => {
101
+ mockFetch.mockResolvedValueOnce(new Response('out of memory', { status: 500 }));
102
+ await expect(invokeOllamaOrchestrator(baseOpts)).rejects.toThrow('smaller numCtx');
103
+ });
104
+ it('handles missing token counts gracefully', async () => {
105
+ mockFetch.mockResolvedValueOnce(okResponse({ message: { content: 'no counts' }, done: true }));
106
+ const result = await invokeOllamaOrchestrator(baseOpts);
107
+ expect(result.inputTokens).toBeNull();
108
+ expect(result.outputTokens).toBeNull();
109
+ });
110
+ it('handles missing message content', async () => {
111
+ mockFetch.mockResolvedValueOnce(okResponse({ message: {}, done: true }));
112
+ const result = await invokeOllamaOrchestrator(baseOpts);
113
+ expect(result.content).toBe('');
114
+ });
115
+ it('wraps non-500 API errors', async () => {
116
+ mockFetch.mockResolvedValueOnce(new Response('model not found', { status: 404 }));
117
+ await expect(invokeOllamaOrchestrator(baseOpts)).rejects.toThrow('[Totem Error] Ollama API error (404)');
118
+ });
119
+ it('throws friendly error on non-JSON response body', async () => {
120
+ mockFetch.mockResolvedValueOnce(new Response('not json at all', { status: 200 }));
121
+ await expect(invokeOllamaOrchestrator(baseOpts)).rejects.toThrow('Ollama returned invalid JSON');
122
+ });
123
+ it('throws friendly error on malformed JSON response', async () => {
124
+ mockFetch.mockResolvedValueOnce(new Response(JSON.stringify([{ unexpected: 'array' }]), {
125
+ status: 200,
126
+ headers: { 'Content-Type': 'application/json' },
127
+ }));
128
+ await expect(invokeOllamaOrchestrator(baseOpts)).rejects.toThrow('Unexpected response from Ollama API');
129
+ });
130
+ });
131
+ //# sourceMappingURL=ollama-orchestrator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama-orchestrator.test.js","sourceRoot":"","sources":["../../src/orchestrators/ollama-orchestrator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { OrchestratorInvokeOptions, OrchestratorResult } from './orchestrator.js';
2
+ /**
3
+ * Invoke an OpenAI-compatible API via the `openai` SDK.
4
+ * Supports OpenAI, Ollama, LM Studio, Groq, OpenRouter, and any
5
+ * server implementing the `/v1/chat/completions` endpoint.
6
+ *
7
+ * @see https://github.com/mmnto-ai/totem/issues/285
8
+ */
9
+ export declare function invokeOpenAIOrchestrator(opts: OrchestratorInvokeOptions & {
10
+ baseUrl?: string;
11
+ }): Promise<OrchestratorResult>;
12
+ //# sourceMappingURL=openai-orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrators/openai-orchestrator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AA6BvF;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,yBAAyB,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD,OAAO,CAAC,kBAAkB,CAAC,CA+C7B"}
@@ -0,0 +1,70 @@
1
+ import { log } from '../ui.js';
2
+ import { detectPackageManager, isQuotaError } from './orchestrator.js';
3
+ // ─── Constants ───────────────────────────────────────
4
+ const DEFAULT_MAX_TOKENS = 16_384;
5
+ /**
6
+ * Dummy API key for local OpenAI-compatible servers (Ollama, LM Studio)
7
+ * that don't require authentication but where the SDK mandates a key.
8
+ */
9
+ const LOCAL_DUMMY_KEY = 'totem-local';
10
+ // ─── SDK loader (BYOSD) ─────────────────────────────
11
+ async function importOpenAISdk() {
12
+ try {
13
+ return (await import('openai')).default;
14
+ }
15
+ catch {
16
+ throw new Error('[Totem Error] OpenAI SDK (openai) is not installed.\n' +
17
+ `Install it with: ${detectPackageManager()} add openai\n` +
18
+ "Or use provider: 'shell' in your orchestrator config.");
19
+ }
20
+ }
21
+ // ─── OpenAI-compatible API orchestrator ─────────────
22
+ /**
23
+ * Invoke an OpenAI-compatible API via the `openai` SDK.
24
+ * Supports OpenAI, Ollama, LM Studio, Groq, OpenRouter, and any
25
+ * server implementing the `/v1/chat/completions` endpoint.
26
+ *
27
+ * @see https://github.com/mmnto-ai/totem/issues/285
28
+ */
29
+ export async function invokeOpenAIOrchestrator(opts) {
30
+ const { prompt, model, tag, baseUrl } = opts;
31
+ // For local servers, use a dummy key if none is set
32
+ const apiKey = process.env['OPENAI_API_KEY'] ?? (baseUrl ? LOCAL_DUMMY_KEY : undefined);
33
+ if (!apiKey) {
34
+ throw new Error('[Totem Error] No OpenAI API key found.\n' +
35
+ 'Set OPENAI_API_KEY in your .env file, or add a baseUrl for local servers (Ollama, LM Studio).');
36
+ }
37
+ const OpenAI = await importOpenAISdk();
38
+ const client = new OpenAI({
39
+ apiKey,
40
+ ...(baseUrl && { baseURL: baseUrl }),
41
+ });
42
+ const serverLabel = baseUrl ? `OpenAI-compatible API (${baseUrl})` : 'OpenAI API';
43
+ log.info(tag, `Invoking ${serverLabel} (this may take 15-60 seconds)...`);
44
+ const startMs = Date.now();
45
+ try {
46
+ const response = await client.chat.completions.create({
47
+ model,
48
+ max_tokens: DEFAULT_MAX_TOKENS,
49
+ messages: [{ role: 'user', content: prompt }],
50
+ });
51
+ const durationMs = Date.now() - startMs;
52
+ const choice = response.choices[0];
53
+ return {
54
+ content: choice?.message?.content ?? '',
55
+ inputTokens: response.usage?.prompt_tokens ?? null,
56
+ outputTokens: response.usage?.completion_tokens ?? null,
57
+ durationMs,
58
+ finishReason: choice?.finish_reason ?? undefined,
59
+ };
60
+ }
61
+ catch (err) {
62
+ if (isQuotaError(err)) {
63
+ err.name = 'QuotaError';
64
+ throw err;
65
+ }
66
+ const msg = err instanceof Error ? err.message : String(err);
67
+ throw new Error(`[Totem Error] OpenAI API call failed: ${msg}`);
68
+ }
69
+ }
70
+ //# sourceMappingURL=openai-orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-orchestrator.js","sourceRoot":"","sources":["../../src/orchestrators/openai-orchestrator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=openai-orchestrator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-orchestrator.test.d.ts","sourceRoot":"","sources":["../../src/orchestrators/openai-orchestrator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,114 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { invokeOpenAIOrchestrator } from './openai-orchestrator.js';
3
+ // ─── Mock SDK ───────────────────────────────────────
4
+ const { mockCreate } = vi.hoisted(() => ({
5
+ mockCreate: vi.fn(),
6
+ }));
7
+ vi.mock('openai', () => ({
8
+ default: class {
9
+ chat = { completions: { create: mockCreate } };
10
+ },
11
+ }));
12
+ vi.mock('../ui.js', () => ({
13
+ log: { info: vi.fn(), success: vi.fn(), warn: vi.fn(), error: vi.fn(), dim: vi.fn() },
14
+ }));
15
+ // ─── Tests ──────────────────────────────────────────
16
+ describe('invokeOpenAIOrchestrator', () => {
17
+ const baseOpts = {
18
+ prompt: 'test prompt',
19
+ model: 'gpt-4o',
20
+ cwd: '.',
21
+ tag: 'Test',
22
+ totemDir: '.totem',
23
+ };
24
+ beforeEach(() => {
25
+ vi.clearAllMocks();
26
+ process.env['OPENAI_API_KEY'] = 'test-key';
27
+ });
28
+ afterEach(() => {
29
+ delete process.env['OPENAI_API_KEY'];
30
+ });
31
+ it('returns structured result from OpenAI API', async () => {
32
+ mockCreate.mockResolvedValueOnce({
33
+ choices: [{ message: { content: 'Hello from GPT' }, finish_reason: 'stop' }],
34
+ usage: { prompt_tokens: 200, completion_tokens: 75 },
35
+ });
36
+ const result = await invokeOpenAIOrchestrator(baseOpts);
37
+ expect(result.content).toBe('Hello from GPT');
38
+ expect(result.inputTokens).toBe(200);
39
+ expect(result.outputTokens).toBe(75);
40
+ expect(result.finishReason).toBe('stop');
41
+ expect(result.durationMs).toBeGreaterThanOrEqual(0);
42
+ });
43
+ it('passes model and prompt to the SDK', async () => {
44
+ mockCreate.mockResolvedValueOnce({
45
+ choices: [{ message: { content: 'ok' }, finish_reason: 'stop' }],
46
+ usage: { prompt_tokens: 10, completion_tokens: 5 },
47
+ });
48
+ await invokeOpenAIOrchestrator(baseOpts);
49
+ expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({
50
+ model: 'gpt-4o',
51
+ messages: [{ role: 'user', content: 'test prompt' }],
52
+ }));
53
+ });
54
+ it('throws when no API key and no baseUrl', async () => {
55
+ delete process.env['OPENAI_API_KEY'];
56
+ await expect(invokeOpenAIOrchestrator(baseOpts)).rejects.toThrow('No OpenAI API key found');
57
+ });
58
+ it('uses dummy key for local servers with baseUrl', async () => {
59
+ delete process.env['OPENAI_API_KEY'];
60
+ mockCreate.mockResolvedValueOnce({
61
+ choices: [{ message: { content: 'local response' }, finish_reason: 'stop' }],
62
+ usage: { prompt_tokens: 10, completion_tokens: 5 },
63
+ });
64
+ const result = await invokeOpenAIOrchestrator({
65
+ ...baseOpts,
66
+ model: 'llama3.1',
67
+ baseUrl: 'http://localhost:11434/v1',
68
+ });
69
+ expect(result.content).toBe('local response');
70
+ });
71
+ it('converts 429 errors to QuotaError', async () => {
72
+ const rateLimitErr = Object.assign(new Error('rate_limit_error'), { status: 429 });
73
+ mockCreate.mockRejectedValueOnce(rateLimitErr);
74
+ try {
75
+ await invokeOpenAIOrchestrator(baseOpts);
76
+ expect.fail('Should have thrown');
77
+ }
78
+ catch (err) {
79
+ expect(err.name).toBe('QuotaError');
80
+ }
81
+ });
82
+ it('wraps other API errors with [Totem Error] prefix', async () => {
83
+ mockCreate.mockRejectedValueOnce(new Error('invalid_api_key'));
84
+ await expect(invokeOpenAIOrchestrator(baseOpts)).rejects.toThrow('[Totem Error] OpenAI API call failed');
85
+ });
86
+ it('handles missing usage gracefully (local servers)', async () => {
87
+ mockCreate.mockResolvedValueOnce({
88
+ choices: [{ message: { content: 'no usage' }, finish_reason: 'stop' }],
89
+ // No usage field — some local servers omit this
90
+ });
91
+ const result = await invokeOpenAIOrchestrator(baseOpts);
92
+ expect(result.content).toBe('no usage');
93
+ expect(result.inputTokens).toBeNull();
94
+ expect(result.outputTokens).toBeNull();
95
+ });
96
+ it('handles empty choices array', async () => {
97
+ mockCreate.mockResolvedValueOnce({
98
+ choices: [],
99
+ usage: { prompt_tokens: 10, completion_tokens: 0 },
100
+ });
101
+ const result = await invokeOpenAIOrchestrator(baseOpts);
102
+ expect(result.content).toBe('');
103
+ expect(result.finishReason).toBeUndefined();
104
+ });
105
+ it('handles null message content', async () => {
106
+ mockCreate.mockResolvedValueOnce({
107
+ choices: [{ message: { content: null }, finish_reason: 'stop' }],
108
+ usage: { prompt_tokens: 10, completion_tokens: 5 },
109
+ });
110
+ const result = await invokeOpenAIOrchestrator(baseOpts);
111
+ expect(result.content).toBe('');
112
+ });
113
+ });
114
+ //# sourceMappingURL=openai-orchestrator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-orchestrator.test.js","sourceRoot":"","sources":["../../src/orchestrators/openai-orchestrator.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrators/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,IAAI,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAMvE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAIjC;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAM7C;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAUlD;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,GACtB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CASrC;AAOD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,kBAAkB,GAC7B,oBAAoB,CA8BtB;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAejF"}
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrators/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,IAAI,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAMvE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAIjC;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAM7C;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAUlD;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,GACtB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CASrC;AAOD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,kBAAkB,GAC7B,oBAAoB,CA8BtB;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CA6BjF"}
@@ -31,7 +31,7 @@ export function isQuotaError(err) {
31
31
  msg.includes('too many requests'));
32
32
  }
33
33
  // ─── Model string parsing (#243) ─────────────────────
34
- const KNOWN_PROVIDERS = ['gemini', 'anthropic', 'shell'];
34
+ const KNOWN_PROVIDERS = ['gemini', 'anthropic', 'openai', 'ollama', 'shell'];
35
35
  /**
36
36
  * Parse a `provider:model` string into its components.
37
37
  * If the prefix before the first colon is a known provider, splits it out.
@@ -91,6 +91,20 @@ export function createOrchestrator(config) {
91
91
  const { invokeAnthropicOrchestrator } = await import('./anthropic-orchestrator.js');
92
92
  return invokeAnthropicOrchestrator(opts);
93
93
  };
94
+ case 'openai':
95
+ return async (opts) => {
96
+ const { invokeOpenAIOrchestrator } = await import('./openai-orchestrator.js');
97
+ return invokeOpenAIOrchestrator({ ...opts, baseUrl: config.baseUrl });
98
+ };
99
+ case 'ollama':
100
+ return async (opts) => {
101
+ const { invokeOllamaOrchestrator } = await import('./ollama-orchestrator.js');
102
+ return invokeOllamaOrchestrator({
103
+ ...opts,
104
+ baseUrl: config.baseUrl,
105
+ numCtx: config.numCtx,
106
+ });
107
+ };
94
108
  }
95
109
  }
96
110
  //# sourceMappingURL=orchestrator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/orchestrators/orchestrator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/orchestrators/orchestrator.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,14 @@ vi.mock('./anthropic-orchestrator.js', () => ({
17
17
  durationMs: 2000,
18
18
  }),
19
19
  }));
20
+ vi.mock('./openai-orchestrator.js', () => ({
21
+ invokeOpenAIOrchestrator: vi.fn().mockResolvedValue({
22
+ content: 'openai result',
23
+ inputTokens: 150,
24
+ outputTokens: 60,
25
+ durationMs: 1500,
26
+ }),
27
+ }));
20
28
  // ─── Tests ──────────────────────────────────────────
21
29
  describe('createOrchestrator', () => {
22
30
  it('returns a function for shell provider', () => {
@@ -69,6 +77,27 @@ describe('createOrchestrator', () => {
69
77
  expect(result.content).toBe('anthropic result');
70
78
  expect(result.inputTokens).toBe(200);
71
79
  });
80
+ it('returns a function for openai provider', () => {
81
+ const config = {
82
+ provider: 'openai',
83
+ defaultModel: 'gpt-4o',
84
+ };
85
+ const invoke = createOrchestrator(config);
86
+ expect(typeof invoke).toBe('function');
87
+ });
88
+ it('openai invoker dispatches to openai-orchestrator module', async () => {
89
+ const config = { provider: 'openai' };
90
+ const invoke = createOrchestrator(config);
91
+ const result = await invoke({
92
+ prompt: 'test',
93
+ model: 'gpt-4o',
94
+ cwd: '.',
95
+ tag: 'Test',
96
+ totemDir: '.totem',
97
+ });
98
+ expect(result.content).toBe('openai result');
99
+ expect(result.inputTokens).toBe(150);
100
+ });
72
101
  });
73
102
  // ─── detectPackageManager ───────────────────────────
74
103
  describe('detectPackageManager', () => {
@@ -182,6 +211,12 @@ describe('parseModelString', () => {
182
211
  model: 'gemini-3.1-pro-preview',
183
212
  });
184
213
  });
214
+ it('parses openai:model into provider and model', () => {
215
+ expect(parseModelString('openai:gpt-4o', 'gemini')).toEqual({
216
+ provider: 'openai',
217
+ model: 'gpt-4o',
218
+ });
219
+ });
185
220
  it('parses shell:model into provider and model', () => {
186
221
  expect(parseModelString('shell:my-model', 'gemini')).toEqual({
187
222
  provider: 'shell',