@agentconnect/cli 0.1.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.
@@ -0,0 +1,509 @@
1
+ import { spawn } from 'child_process';
2
+ import path from 'path';
3
+ import { buildInstallCommandAuto, buildLoginCommand, buildStatusCommand, checkCommandVersion, commandExists, createLineParser, debugLog, resolveWindowsCommand, resolveCommandPath, runCommand, } from './utils.js';
4
+ const CODEX_PACKAGE = '@openai/codex';
5
+ const DEFAULT_LOGIN = 'codex login';
6
+ const DEFAULT_STATUS = 'codex login status';
7
+ const CODEX_MODELS_CACHE_TTL_MS = 60_000;
8
+ let codexModelsCache = null;
9
+ let codexModelsCacheAt = 0;
10
+ function trimOutput(value, limit = 400) {
11
+ const cleaned = value.trim();
12
+ if (!cleaned)
13
+ return '';
14
+ if (cleaned.length <= limit)
15
+ return cleaned;
16
+ return `${cleaned.slice(0, limit)}...`;
17
+ }
18
+ function buildCodexExecArgs(options) {
19
+ const { prompt, cdTarget, resumeSessionId, model, reasoningEffort, mode } = options;
20
+ const args = ['exec', '--skip-git-repo-check'];
21
+ if (mode === 'legacy') {
22
+ args.push('--json', '-C', cdTarget);
23
+ }
24
+ else {
25
+ args.push('--experimental-json', '--cd', cdTarget);
26
+ }
27
+ args.push('--yolo');
28
+ if (model) {
29
+ args.push('--model', String(model));
30
+ }
31
+ if (reasoningEffort) {
32
+ args.push('--config', `model_reasoning_effort=${reasoningEffort}`);
33
+ }
34
+ if (resumeSessionId) {
35
+ args.push('resume', resumeSessionId);
36
+ }
37
+ args.push(prompt);
38
+ return args;
39
+ }
40
+ function shouldFallbackToLegacy(lines) {
41
+ const combined = lines.join(' ').toLowerCase();
42
+ if (!(combined.includes('unknown flag') ||
43
+ combined.includes('unknown option') ||
44
+ combined.includes('unrecognized option') ||
45
+ combined.includes('unknown argument') ||
46
+ combined.includes('unexpected argument') ||
47
+ combined.includes('invalid option') ||
48
+ combined.includes('invalid argument'))) {
49
+ return false;
50
+ }
51
+ return combined.includes('experimental-json') || combined.includes('--cd');
52
+ }
53
+ export function getCodexCommand() {
54
+ const override = process.env.AGENTCONNECT_CODEX_COMMAND;
55
+ const base = override || 'codex';
56
+ const resolved = resolveCommandPath(base);
57
+ return resolved || resolveWindowsCommand(base);
58
+ }
59
+ export async function ensureCodexInstalled() {
60
+ const command = getCodexCommand();
61
+ const versionCheck = await checkCommandVersion(command, [['--version'], ['-V']]);
62
+ if (versionCheck.ok) {
63
+ return { installed: true, version: versionCheck.version || undefined };
64
+ }
65
+ if (commandExists(command)) {
66
+ return { installed: true, version: undefined };
67
+ }
68
+ const install = await buildInstallCommandAuto(CODEX_PACKAGE);
69
+ if (!install.command) {
70
+ return { installed: false, version: undefined, packageManager: install.packageManager };
71
+ }
72
+ debugLog('Codex', 'install', { command: install.command, args: install.args });
73
+ const installResult = await runCommand(install.command, install.args, {
74
+ shell: process.platform === 'win32',
75
+ });
76
+ debugLog('Codex', 'install-result', {
77
+ code: installResult.code,
78
+ stderr: trimOutput(installResult.stderr),
79
+ });
80
+ const after = await checkCommandVersion(command, [['--version'], ['-V']]);
81
+ // Invalidate models cache after installation so fresh models are fetched
82
+ codexModelsCache = null;
83
+ codexModelsCacheAt = 0;
84
+ return {
85
+ installed: after.ok,
86
+ version: after.version || undefined,
87
+ packageManager: install.packageManager,
88
+ };
89
+ }
90
+ export async function getCodexStatus() {
91
+ const command = getCodexCommand();
92
+ const versionCheck = await checkCommandVersion(command, [['--version'], ['-V']]);
93
+ const installed = versionCheck.ok || commandExists(command);
94
+ let loggedIn = false;
95
+ if (installed) {
96
+ const status = buildStatusCommand('AGENTCONNECT_CODEX_STATUS', DEFAULT_STATUS);
97
+ if (status.command) {
98
+ const statusCommand = resolveWindowsCommand(status.command);
99
+ const result = await runCommand(statusCommand, status.args);
100
+ loggedIn = result.code === 0;
101
+ }
102
+ }
103
+ return { installed, loggedIn, version: versionCheck.version || undefined };
104
+ }
105
+ export async function loginCodex() {
106
+ const login = buildLoginCommand('AGENTCONNECT_CODEX_LOGIN', DEFAULT_LOGIN);
107
+ if (!login.command) {
108
+ return { loggedIn: false };
109
+ }
110
+ const command = resolveWindowsCommand(login.command);
111
+ debugLog('Codex', 'login', { command, args: login.args });
112
+ const result = await runCommand(command, login.args, { env: { ...process.env, CI: '1' } });
113
+ debugLog('Codex', 'login-result', { code: result.code, stderr: trimOutput(result.stderr) });
114
+ const status = await getCodexStatus();
115
+ codexModelsCache = null;
116
+ codexModelsCacheAt = 0;
117
+ return { loggedIn: status.loggedIn };
118
+ }
119
+ function safeJsonParse(line) {
120
+ try {
121
+ return JSON.parse(line);
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ function extractSessionId(ev) {
128
+ const t = String(ev.type ?? '');
129
+ if (t === 'thread.started') {
130
+ return typeof ev.thread_id === 'string' ? ev.thread_id : null;
131
+ }
132
+ const id = ev.thread_id ?? ev.threadId ?? ev.session_id ?? ev.sessionId;
133
+ return typeof id === 'string' ? id : null;
134
+ }
135
+ function extractUsage(ev) {
136
+ const usage = ev.usage ?? ev.token_usage ?? ev.tokens ?? ev.tokenUsage;
137
+ if (!usage || typeof usage !== 'object')
138
+ return null;
139
+ const toNumber = (v) => typeof v === 'number' && Number.isFinite(v) ? v : undefined;
140
+ const input = toNumber(usage.input_tokens ?? usage.prompt_tokens ?? usage.inputTokens ?? usage.promptTokens);
141
+ const output = toNumber(usage.output_tokens ?? usage.completion_tokens ?? usage.outputTokens ?? usage.completionTokens);
142
+ const total = toNumber(usage.total_tokens ?? usage.totalTokens);
143
+ const cached = toNumber(usage.cached_input_tokens ?? usage.cachedInputTokens);
144
+ const out = {};
145
+ if (input !== undefined)
146
+ out.input_tokens = input;
147
+ if (output !== undefined)
148
+ out.output_tokens = output;
149
+ if (total !== undefined)
150
+ out.total_tokens = total;
151
+ if (cached !== undefined)
152
+ out.cached_input_tokens = cached;
153
+ return Object.keys(out).length ? out : null;
154
+ }
155
+ function normalizeItem(raw) {
156
+ if (!raw || typeof raw !== 'object')
157
+ return raw;
158
+ const item = raw;
159
+ const type = item.type;
160
+ const id = item.id;
161
+ if (type === 'command_execution' && id) {
162
+ return {
163
+ id,
164
+ type,
165
+ command: item.command || '',
166
+ aggregated_output: item.aggregated_output,
167
+ exit_code: item.exit_code,
168
+ status: item.status,
169
+ };
170
+ }
171
+ if (type === 'reasoning' && id) {
172
+ return { id, type, text: item.text || '' };
173
+ }
174
+ if (type === 'agent_message' && id) {
175
+ return { id, type, text: item.text || '' };
176
+ }
177
+ return raw;
178
+ }
179
+ function normalizeEvent(raw) {
180
+ const type = typeof raw.type === 'string' ? raw.type : 'unknown';
181
+ if (type === 'item.started' || type === 'item.completed') {
182
+ return { type, item: normalizeItem(raw.item) };
183
+ }
184
+ if (type === 'agent_message') {
185
+ return { type, text: raw.text || '' };
186
+ }
187
+ if (type === 'error') {
188
+ return { type, message: raw.message || 'Unknown error' };
189
+ }
190
+ return { ...raw, type };
191
+ }
192
+ function isTerminalEvent(ev) {
193
+ const t = String(ev.type ?? '');
194
+ return t === 'turn.completed' || t === 'turn.failed';
195
+ }
196
+ function normalizeEffortId(raw) {
197
+ if (!raw)
198
+ return null;
199
+ return String(raw).trim().toLowerCase();
200
+ }
201
+ function formatEffortLabel(id) {
202
+ if (!id)
203
+ return '';
204
+ const normalized = String(id).trim().toLowerCase();
205
+ if (normalized === 'xhigh')
206
+ return 'X-High';
207
+ if (normalized === 'none')
208
+ return 'None';
209
+ if (normalized === 'minimal')
210
+ return 'Minimal';
211
+ if (normalized === 'low')
212
+ return 'Low';
213
+ if (normalized === 'medium')
214
+ return 'Medium';
215
+ if (normalized === 'high')
216
+ return 'High';
217
+ return normalized.toUpperCase();
218
+ }
219
+ function normalizeEffortOptions(raw) {
220
+ if (!Array.isArray(raw))
221
+ return [];
222
+ const options = raw
223
+ .map((entry) => {
224
+ if (!entry || typeof entry !== 'object')
225
+ return null;
226
+ const opt = entry;
227
+ const id = normalizeEffortId(opt.reasoning_effort ?? opt.reasoningEffort ?? opt.effort ?? opt.level);
228
+ if (!id)
229
+ return null;
230
+ const label = formatEffortLabel(id);
231
+ return { id, label };
232
+ })
233
+ .filter((x) => x !== null);
234
+ const seen = new Set();
235
+ return options.filter((option) => {
236
+ if (seen.has(option.id))
237
+ return false;
238
+ seen.add(option.id);
239
+ return true;
240
+ });
241
+ }
242
+ function normalizeCodexModels(raw) {
243
+ const response = raw;
244
+ const list = Array.isArray(response?.data)
245
+ ? response.data
246
+ : Array.isArray(response?.items)
247
+ ? response.items
248
+ : [];
249
+ const mapped = [];
250
+ for (const item of list) {
251
+ if (!item || typeof item !== 'object')
252
+ continue;
253
+ const id = item.id || item.model;
254
+ if (!id)
255
+ continue;
256
+ const displayName = item.displayName || item.display_name || item.name || item.title || String(id);
257
+ const reasoningEfforts = normalizeEffortOptions(item.supportedReasoningEfforts ||
258
+ item.supported_reasoning_efforts ||
259
+ item.supportedReasoningLevels ||
260
+ item.supported_reasoning_levels);
261
+ const defaultReasoningEffort = normalizeEffortId(item.defaultReasoningEffort || item.default_reasoning_effort);
262
+ mapped.push({
263
+ id: String(id),
264
+ provider: 'codex',
265
+ displayName: String(displayName),
266
+ reasoningEfforts: reasoningEfforts.length ? reasoningEfforts : undefined,
267
+ defaultReasoningEffort: defaultReasoningEffort || undefined,
268
+ });
269
+ }
270
+ if (!mapped.length)
271
+ return [];
272
+ const seen = new Set();
273
+ return mapped.filter((model) => {
274
+ const key = model.id;
275
+ if (seen.has(key))
276
+ return false;
277
+ seen.add(key);
278
+ return true;
279
+ });
280
+ }
281
+ async function fetchCodexModels(command) {
282
+ return new Promise((resolve) => {
283
+ const child = spawn(command, ['app-server'], {
284
+ env: { ...process.env },
285
+ stdio: ['pipe', 'pipe', 'pipe'],
286
+ });
287
+ let settled = false;
288
+ const initId = 1;
289
+ const listId = 2;
290
+ const timeout = setTimeout(() => {
291
+ finalize(null);
292
+ }, 8000);
293
+ const finalize = (models) => {
294
+ if (settled)
295
+ return;
296
+ settled = true;
297
+ clearTimeout(timeout);
298
+ child.kill('SIGTERM');
299
+ resolve(models);
300
+ };
301
+ const handleLine = (line) => {
302
+ const parsed = safeJsonParse(line);
303
+ if (!parsed || typeof parsed !== 'object')
304
+ return;
305
+ if (parsed.id === listId && parsed.result) {
306
+ finalize(normalizeCodexModels(parsed.result));
307
+ }
308
+ if (parsed.id === listId && parsed.error) {
309
+ finalize(null);
310
+ }
311
+ };
312
+ const stdoutParser = createLineParser(handleLine);
313
+ child.stdout?.on('data', stdoutParser);
314
+ child.stderr?.on('data', () => { });
315
+ child.on('error', () => finalize(null));
316
+ child.on('close', () => finalize(null));
317
+ if (!child.stdin) {
318
+ finalize(null);
319
+ return;
320
+ }
321
+ const initialize = {
322
+ method: 'initialize',
323
+ id: initId,
324
+ params: {
325
+ clientInfo: { name: 'agentconnect', title: 'AgentConnect', version: '0.1.0' },
326
+ },
327
+ };
328
+ const initialized = { method: 'initialized' };
329
+ const listRequest = { method: 'model/list', id: listId, params: { cursor: null, limit: null } };
330
+ const payload = `${JSON.stringify(initialize)}\n${JSON.stringify(initialized)}\n${JSON.stringify(listRequest)}\n`;
331
+ child.stdin.write(payload);
332
+ });
333
+ }
334
+ export async function listCodexModels() {
335
+ if (codexModelsCache && Date.now() - codexModelsCacheAt < CODEX_MODELS_CACHE_TTL_MS) {
336
+ return codexModelsCache;
337
+ }
338
+ const command = getCodexCommand();
339
+ const versionCheck = await checkCommandVersion(command, [['--version'], ['-V']]);
340
+ if (!versionCheck.ok) {
341
+ // Codex not installed - return empty, don't cache
342
+ return [];
343
+ }
344
+ const models = await fetchCodexModels(command);
345
+ if (models && models.length) {
346
+ codexModelsCache = models;
347
+ codexModelsCacheAt = Date.now();
348
+ return models;
349
+ }
350
+ // Fetch failed - return empty, don't cache so it retries next time
351
+ return [];
352
+ }
353
+ export function runCodexPrompt({ prompt, resumeSessionId, model, reasoningEffort, repoRoot, cwd, onEvent, signal, }) {
354
+ return new Promise((resolve) => {
355
+ const command = getCodexCommand();
356
+ const resolvedRepoRoot = repoRoot ? path.resolve(repoRoot) : null;
357
+ const resolvedCwd = cwd ? path.resolve(cwd) : null;
358
+ const runDir = resolvedCwd || resolvedRepoRoot || process.cwd();
359
+ const cdTarget = resolvedRepoRoot || resolvedCwd || '.';
360
+ const runAttempt = (mode) => new Promise((attemptResolve) => {
361
+ const args = buildCodexExecArgs({
362
+ prompt,
363
+ cdTarget,
364
+ resumeSessionId,
365
+ model,
366
+ reasoningEffort,
367
+ mode,
368
+ });
369
+ const argsPreview = [...args];
370
+ if (argsPreview.length > 0) {
371
+ argsPreview[argsPreview.length - 1] = '[prompt]';
372
+ }
373
+ debugLog('Codex', 'spawn', { command, args: argsPreview, cwd: runDir, mode });
374
+ const child = spawn(command, args, {
375
+ cwd: runDir,
376
+ env: { ...process.env },
377
+ stdio: ['ignore', 'pipe', 'pipe'],
378
+ });
379
+ if (signal) {
380
+ signal.addEventListener('abort', () => {
381
+ child.kill('SIGTERM');
382
+ });
383
+ }
384
+ let aggregated = '';
385
+ let finalSessionId = null;
386
+ let didFinalize = false;
387
+ let sawError = false;
388
+ const emitError = (message) => {
389
+ if (sawError)
390
+ return;
391
+ sawError = true;
392
+ onEvent({ type: 'error', message });
393
+ };
394
+ let sawJson = false;
395
+ const stdoutLines = [];
396
+ const stderrLines = [];
397
+ const pushLine = (list, line) => {
398
+ if (!line)
399
+ return;
400
+ list.push(line);
401
+ if (list.length > 12)
402
+ list.shift();
403
+ };
404
+ const handleLine = (line, source) => {
405
+ const parsed = safeJsonParse(line);
406
+ if (!parsed || typeof parsed !== 'object') {
407
+ if (source === 'stdout') {
408
+ pushLine(stdoutLines, line);
409
+ }
410
+ else {
411
+ pushLine(stderrLines, line);
412
+ }
413
+ return;
414
+ }
415
+ sawJson = true;
416
+ const ev = parsed;
417
+ const normalized = normalizeEvent(ev);
418
+ const sid = extractSessionId(ev);
419
+ if (sid)
420
+ finalSessionId = sid;
421
+ const usage = extractUsage(ev);
422
+ if (usage) {
423
+ onEvent({
424
+ type: 'usage',
425
+ inputTokens: usage.input_tokens,
426
+ outputTokens: usage.output_tokens,
427
+ });
428
+ }
429
+ if (normalized.type === 'agent_message') {
430
+ const text = normalized.text;
431
+ if (typeof text === 'string' && text) {
432
+ aggregated += text;
433
+ onEvent({ type: 'delta', text });
434
+ }
435
+ }
436
+ else if (normalized.type === 'item.completed') {
437
+ const item = normalized.item;
438
+ if (item && typeof item === 'object') {
439
+ if (item.type === 'command_execution' && typeof item.aggregated_output === 'string') {
440
+ onEvent({ type: 'delta', text: item.aggregated_output });
441
+ }
442
+ if (item.type === 'agent_message' && typeof item.text === 'string') {
443
+ aggregated += item.text;
444
+ onEvent({ type: 'delta', text: item.text });
445
+ }
446
+ }
447
+ }
448
+ if (isTerminalEvent(ev) && !didFinalize) {
449
+ if (ev.type === 'turn.failed') {
450
+ const message = ev.error?.message;
451
+ emitError(typeof message === 'string' ? message : 'Codex run failed');
452
+ didFinalize = true;
453
+ return;
454
+ }
455
+ if (!sawError) {
456
+ didFinalize = true;
457
+ onEvent({ type: 'final', text: aggregated });
458
+ }
459
+ }
460
+ };
461
+ const stdoutParser = createLineParser((line) => handleLine(line, 'stdout'));
462
+ const stderrParser = createLineParser((line) => handleLine(line, 'stderr'));
463
+ child.stdout?.on('data', stdoutParser);
464
+ child.stderr?.on('data', stderrParser);
465
+ child.on('close', (code) => {
466
+ if (!didFinalize) {
467
+ if (code && code !== 0) {
468
+ const hint = stderrLines.at(-1) || stdoutLines.at(-1) || '';
469
+ const suffix = hint ? `: ${hint}` : '';
470
+ const fallback = mode === 'modern' && !sawJson && shouldFallbackToLegacy([
471
+ ...stderrLines,
472
+ ...stdoutLines,
473
+ ]);
474
+ debugLog('Codex', 'exit', {
475
+ code,
476
+ stderr: stderrLines,
477
+ stdout: stdoutLines,
478
+ fallback,
479
+ });
480
+ if (fallback) {
481
+ attemptResolve({ sessionId: finalSessionId, fallback: true });
482
+ return;
483
+ }
484
+ emitError(`Codex exited with code ${code}${suffix}`);
485
+ }
486
+ else if (!sawError) {
487
+ onEvent({ type: 'final', text: aggregated });
488
+ }
489
+ }
490
+ attemptResolve({ sessionId: finalSessionId, fallback: false });
491
+ });
492
+ child.on('error', (err) => {
493
+ debugLog('Codex', 'spawn-error', { message: err?.message });
494
+ emitError(err?.message ?? 'Codex failed to start');
495
+ attemptResolve({ sessionId: finalSessionId, fallback: false });
496
+ });
497
+ });
498
+ void (async () => {
499
+ const primary = await runAttempt('modern');
500
+ if (primary.fallback) {
501
+ debugLog('Codex', 'fallback', { from: 'modern', to: 'legacy' });
502
+ const legacy = await runAttempt('legacy');
503
+ resolve({ sessionId: legacy.sessionId });
504
+ return;
505
+ }
506
+ resolve({ sessionId: primary.sessionId });
507
+ })();
508
+ });
509
+ }
@@ -0,0 +1,5 @@
1
+ import type { Provider, ProviderId, ModelInfo } from '../types.js';
2
+ export declare const providers: Record<ProviderId, Provider>;
3
+ export declare function listModels(): Promise<ModelInfo[]>;
4
+ export declare function listRecentModels(providerId?: ProviderId): Promise<ModelInfo[]>;
5
+ export declare function resolveProviderForModel(model: string | undefined): ProviderId;
@@ -0,0 +1,90 @@
1
+ import { ensureClaudeInstalled, getClaudeStatus, listClaudeModels, listClaudeRecentModels, loginClaude, runClaudePrompt, } from './claude.js';
2
+ import { ensureCodexInstalled, getCodexStatus, listCodexModels, loginCodex, runCodexPrompt, } from './codex.js';
3
+ import { ensureLocalInstalled, getLocalStatus, listLocalModels, loginLocal, runLocalPrompt, } from './local.js';
4
+ export const providers = {
5
+ claude: {
6
+ id: 'claude',
7
+ name: 'Claude',
8
+ ensureInstalled: ensureClaudeInstalled,
9
+ status: getClaudeStatus,
10
+ login: loginClaude,
11
+ logout: async () => { },
12
+ runPrompt: runClaudePrompt,
13
+ },
14
+ codex: {
15
+ id: 'codex',
16
+ name: 'Codex',
17
+ ensureInstalled: ensureCodexInstalled,
18
+ status: getCodexStatus,
19
+ login: loginCodex,
20
+ logout: async () => { },
21
+ runPrompt: runCodexPrompt,
22
+ },
23
+ local: {
24
+ id: 'local',
25
+ name: 'Local',
26
+ ensureInstalled: ensureLocalInstalled,
27
+ status: getLocalStatus,
28
+ login: loginLocal,
29
+ logout: async () => { },
30
+ runPrompt: runLocalPrompt,
31
+ },
32
+ };
33
+ export async function listModels() {
34
+ const claudeModels = await listClaudeModels();
35
+ const codexModels = await listCodexModels();
36
+ const base = [
37
+ ...claudeModels,
38
+ { id: 'local', provider: 'local', displayName: 'Local Model' },
39
+ ];
40
+ const envModels = process.env.AGENTCONNECT_LOCAL_MODELS;
41
+ if (envModels) {
42
+ try {
43
+ const parsed = JSON.parse(envModels);
44
+ if (Array.isArray(parsed)) {
45
+ for (const entry of parsed) {
46
+ if (typeof entry === 'string' && entry) {
47
+ base.push({ id: entry, provider: 'local', displayName: entry });
48
+ }
49
+ }
50
+ }
51
+ }
52
+ catch {
53
+ // ignore invalid json
54
+ }
55
+ }
56
+ const discovered = await listLocalModels();
57
+ const all = [...base, ...codexModels, ...discovered.filter((entry) => entry.id !== 'local')];
58
+ const seen = new Set();
59
+ return all.filter((entry) => {
60
+ const key = `${entry.provider}:${entry.id}`;
61
+ if (seen.has(key))
62
+ return false;
63
+ seen.add(key);
64
+ return true;
65
+ });
66
+ }
67
+ export async function listRecentModels(providerId) {
68
+ if (providerId && providerId !== 'claude')
69
+ return [];
70
+ const recent = await listClaudeRecentModels();
71
+ return recent.filter((entry) => entry.provider === 'claude');
72
+ }
73
+ export function resolveProviderForModel(model) {
74
+ const lower = String(model || '').toLowerCase();
75
+ if (lower.includes('codex'))
76
+ return 'codex';
77
+ if (lower.startsWith('gpt') ||
78
+ lower.startsWith('o1') ||
79
+ lower.startsWith('o3') ||
80
+ lower.startsWith('o4')) {
81
+ return 'codex';
82
+ }
83
+ if (lower.includes('local'))
84
+ return 'local';
85
+ if (lower.includes('opus') || lower.includes('sonnet') || lower.includes('haiku'))
86
+ return 'claude';
87
+ if (lower.includes('claude'))
88
+ return 'claude';
89
+ return 'claude';
90
+ }
@@ -0,0 +1,8 @@
1
+ import type { ProviderStatus, ProviderLoginOptions, ModelInfo, RunPromptOptions, RunPromptResult, InstallResult } from '../types.js';
2
+ export declare function ensureLocalInstalled(): Promise<InstallResult>;
3
+ export declare function getLocalStatus(): Promise<ProviderStatus>;
4
+ export declare function loginLocal(options?: ProviderLoginOptions): Promise<{
5
+ loggedIn: boolean;
6
+ }>;
7
+ export declare function listLocalModels(): Promise<ModelInfo[]>;
8
+ export declare function runLocalPrompt({ prompt, model, onEvent, }: RunPromptOptions): Promise<RunPromptResult>;