@nodeint/migent 0.1.5 → 0.1.7
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.
- package/README.md +195 -151
- package/dist/commands/agent.d.ts +8 -1
- package/dist/commands/agent.js +292 -19
- package/dist/commands/config.d.ts +3 -3
- package/dist/commands/config.js +57 -55
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/execute.d.ts +1 -2
- package/dist/commands/execute.js +95 -26
- package/dist/commands/init.d.ts +3 -3
- package/dist/commands/init.js +20 -20
- package/dist/commands/prepare.js +1 -0
- package/dist/commands/provider.d.ts +10 -0
- package/dist/commands/provider.js +77 -0
- package/dist/commands/sessions.js +11 -10
- package/dist/commands/skill.js +8 -1
- package/dist/display.d.ts +17 -0
- package/dist/display.js +43 -1
- package/dist/exits.d.ts +3 -1
- package/dist/exits.js +9 -2
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.js +1 -0
- package/dist/index.js +1 -0
- package/dist/migrate.d.ts +3 -3
- package/dist/migrate.js +26 -24
- package/dist/program.js +78 -31
- package/dist/provider.d.ts +13 -11
- package/dist/provider.js +38 -33
- package/dist/session-row.d.ts +2 -0
- package/dist/session-row.js +2 -0
- package/dist/templates/migent/SKILL.md +159 -157
- package/dist/templates/migent/references/config.md +56 -0
- package/dist/templates/migent/references/inspect.md +32 -0
- package/dist/templates/migent/references/schema-config.yaml +10 -1
- package/dist/templates/migent/references/schema-workflow.yaml +12 -2
- package/dist/templates/migent/references/sessions.md +65 -0
- package/package.json +4 -4
package/dist/program.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { ConfigurationError, DEFAULT_MAX_DISPATCHES } from "@nodeint/migent-kernel";
|
|
4
|
-
import { dispatchAgent } from "./commands/agent.js";
|
|
3
|
+
import { ConfigurationError, DEFAULT_MAX_DISPATCHES, requireAgentSession } from "@nodeint/migent-kernel";
|
|
4
|
+
import { continueAgent, dispatchAgent } from "./commands/agent.js";
|
|
5
5
|
import { runConfigModel, runConfigRole, runConfigShow } from "./commands/config.js";
|
|
6
6
|
import { runDoctor } from "./commands/doctor.js";
|
|
7
7
|
import { executeWorkflow } from "./commands/execute.js";
|
|
@@ -11,6 +11,7 @@ import { runInit } from "./commands/init.js";
|
|
|
11
11
|
import { runSessionMetrics, runSessions } from "./commands/sessions.js";
|
|
12
12
|
import { parsePort, runServiceStart, runServiceStatus, runServiceStop } from "./commands/service.js";
|
|
13
13
|
import { installSkill, updateSkill } from "./commands/skill.js";
|
|
14
|
+
import { runProviderList } from "./commands/provider.js";
|
|
14
15
|
import { runRegister, runRemove, runWorkspaceList } from "./commands/workspace-registry.js";
|
|
15
16
|
import { printError } from "./display.js";
|
|
16
17
|
import { LOOP_EXITS, STAGE_EXITS } from "./exits.js";
|
|
@@ -20,7 +21,7 @@ import { PACKAGE_VERSION } from "./version.js";
|
|
|
20
21
|
import { findWorkspace, invocationCwdPath } from "./workspace.js";
|
|
21
22
|
const PRIOR_HELP = "Completed session with the workflow id and decision named in requires. Checked before outputs are injected.";
|
|
22
23
|
const PROGRESS_HELP = "Print each tool call when it finishes. The default prints one line when a stage starts and one when it finishes.";
|
|
23
|
-
const VERBOSE_HELP = "
|
|
24
|
+
const VERBOSE_HELP = "Accepted for compatibility. Stage lines already include the attempt and the time. The response stays in the execution directory.";
|
|
24
25
|
export function createProgram(status) {
|
|
25
26
|
const program = new Command();
|
|
26
27
|
program
|
|
@@ -127,6 +128,18 @@ export function createProgram(status) {
|
|
|
127
128
|
.option("--json", "Print the checks as one JSON object.")
|
|
128
129
|
.addHelpText("after", exitText(["Exit 0 when every check passes.", "Exit 1 when a check fails."]))
|
|
129
130
|
.action((options) => settle(status, () => runDoctor(findWorkspace(invocationCwdPath()), { asJson: options.json === true })));
|
|
131
|
+
const provider = program.command("provider").description("List registered provider providers and their models.");
|
|
132
|
+
provider
|
|
133
|
+
.command("list")
|
|
134
|
+
.description("List each registered provider, whether its CLI is on PATH, and the models it reports.")
|
|
135
|
+
.option("--json", "Print the provider list as one JSON object.")
|
|
136
|
+
.addHelpText("after", exitText([
|
|
137
|
+
"Exit 0 when the command ran and at least one provider returned models.",
|
|
138
|
+
"Exit 1 when no provider returned any models.",
|
|
139
|
+
"A missing optional CLI is not an error.",
|
|
140
|
+
]))
|
|
141
|
+
.action((options) => settle(status, () => runProviderList({ asJson: options.json === true })));
|
|
142
|
+
showHelpWhenBare(status, provider);
|
|
130
143
|
program
|
|
131
144
|
.command("migrate")
|
|
132
145
|
.description("Upgrade config and workflows to the current schema version.")
|
|
@@ -195,10 +208,11 @@ export function createProgram(status) {
|
|
|
195
208
|
return program;
|
|
196
209
|
}
|
|
197
210
|
const ROLE_EXITS = [
|
|
198
|
-
"Exit 0 when the outcome is complete.",
|
|
211
|
+
"Exit 0 when the outcome is complete, or the role session is already completed.",
|
|
199
212
|
"Exit 1 when the outcome is not complete.",
|
|
200
213
|
"Exit 2 on a configuration error.",
|
|
201
|
-
"Exit
|
|
214
|
+
"Exit 6 when a cancelled role session reopened more than one execution and one exhausted verify.",
|
|
215
|
+
"Exit 130 when cancelled. session continue resumes that execution.",
|
|
202
216
|
];
|
|
203
217
|
const RUN_SPLIT = "Pass --workflow for a workflow run, or --role for an agent run.";
|
|
204
218
|
function registerSessionCommand(program, status) {
|
|
@@ -218,26 +232,26 @@ function registerSessionCommand(program, status) {
|
|
|
218
232
|
.option("--prompt <prompt>", "Prompt text for a role run.")
|
|
219
233
|
.option("--file <file>", "Read the role prompt from this file.")
|
|
220
234
|
.option("--stdin", "Read the role prompt from stdin.")
|
|
221
|
-
.option("--json", "Print the role result as one JSON object.
|
|
222
|
-
.addHelpText("after", `With --workflow:\n${exitText(LOOP_EXITS)}With --workflow and --once:\n${exitText(STAGE_EXITS)}Without --workflow:\n${exitText(ROLE_EXITS)}Examples:\n migent session run --workflow plan --task "<one line>"\n migent session run --session
|
|
235
|
+
.option("--json", "Print the role result as one JSON object.")
|
|
236
|
+
.addHelpText("after", `With --workflow:\n${exitText(LOOP_EXITS)}With --workflow and --once:\n${exitText(STAGE_EXITS)}Without --workflow:\n${exitText(ROLE_EXITS)}Examples:\n migent session run --workflow plan --task "<one line>"\n migent session run --session <session> --workflow plan\n migent session run --role reviewer --prompt "<prompt>"\n`)
|
|
223
237
|
.action((options) => settle(status, () => runSessionCommand(options)));
|
|
224
238
|
session
|
|
225
239
|
.command("prepare")
|
|
226
240
|
.description("Create a prepared session and exit. Write task.md, then session run --session <id> --workflow <workflow>.")
|
|
227
|
-
.argument("<
|
|
241
|
+
.argument("<title>", "Session title shown in the list, such as \"some task title\". The session id is a UUID.")
|
|
228
242
|
.requiredOption("--workflow <workflow>", "Workflow id, or a path to a workflow file.")
|
|
229
243
|
.option("--prior <session>", PRIOR_HELP)
|
|
230
|
-
.addHelpText("after", `${exitText(["Exit 0 when the session is prepared.", "Exit 2 on a configuration error."])}Examples:\n migent session prepare
|
|
244
|
+
.addHelpText("after", `${exitText(["Exit 0 when the session is prepared.", "Exit 2 on a configuration error."])}Examples:\n migent session prepare "some task title" --workflow plan\n`)
|
|
231
245
|
.action((id, options) => settle(status, () => prepareWorkflowSession(findWorkspace(invocationCwdPath()), id, String(options.workflow), optionalText(options.prior))));
|
|
232
246
|
const continueCommand = session
|
|
233
247
|
.command("continue")
|
|
234
|
-
.description("Resume
|
|
248
|
+
.description("Resume a stopped workflow or role session. A workflow session runs until it stops and resets revision counts. --once dispatches one workflow stage and does not reset them.")
|
|
235
249
|
.argument("<session>", "Session to resume.");
|
|
236
250
|
addDispatchOptions(continueCommand, "Dispatch the next eligible stage once and do not reset revision counts. Without --once, continue resets them.");
|
|
237
251
|
continueCommand
|
|
238
252
|
.option("--reply <text>", "Reply for the blocked stage. The new execution prompt body is this text.")
|
|
239
253
|
.option("--reply-file <file>", "Read the blocked stage's reply from this file.")
|
|
240
|
-
.addHelpText("after",
|
|
254
|
+
.addHelpText("after", `Workflow session:\n${exitText(LOOP_EXITS)}With --once:\n${exitText(STAGE_EXITS)}Role session:\n${exitText(ROLE_EXITS)}Examples:\n migent session continue <session>\n migent session continue <session> --once\n migent session continue <session> --reply "<reply>"\n migent session continue <session> --stage plan --reply-file answer.md\n`)
|
|
241
255
|
.action((id, options) => settle(status, () => continueSession(id, options)));
|
|
242
256
|
session
|
|
243
257
|
.command("repair")
|
|
@@ -290,15 +304,15 @@ function registerSessionCommand(program, status) {
|
|
|
290
304
|
})));
|
|
291
305
|
session
|
|
292
306
|
.command("metrics")
|
|
293
|
-
.description("Print
|
|
294
|
-
.argument("[session]", "
|
|
307
|
+
.description("Print usage, time, and revision counts recorded for a completed workflow session.")
|
|
308
|
+
.argument("[session]", "Workflow session to read. Omit to list sessions that have metrics.")
|
|
295
309
|
.option("--json", "Print one JSON object, or one object per line for a list.")
|
|
296
310
|
.option("--workflow <id>", "With a list, keep sessions for this workflow.")
|
|
297
311
|
.option("--status <status>", "With a list, keep sessions in this status.")
|
|
298
312
|
.option("--limit <n>", "With a list, keep the newest n sessions.")
|
|
299
313
|
.addHelpText("after", `${exitText([
|
|
300
314
|
"Exit 0 after one session object, or one object per line. An empty list prints nothing.",
|
|
301
|
-
"Exit 2 when --json is missing, the session has no
|
|
315
|
+
"Exit 2 when --json is missing, the session has no metrics.json, that file is corrupt, or a list flag is invalid.",
|
|
302
316
|
])}Examples:\n migent session metrics <session> --json\n migent session metrics --json --workflow <id> --status completed --limit 10\n`)
|
|
303
317
|
.action((id, options) => settle(status, () => runSessionMetrics(findWorkspace(invocationCwdPath()), id, {
|
|
304
318
|
asJson: options.json === true,
|
|
@@ -393,7 +407,6 @@ function workflowFlagsPresent(options) {
|
|
|
393
407
|
return (options.once === true ||
|
|
394
408
|
options.stage !== undefined ||
|
|
395
409
|
options.prior !== undefined ||
|
|
396
|
-
options.attempts !== undefined ||
|
|
397
410
|
options.maxDispatches !== undefined ||
|
|
398
411
|
options.unlimitedDispatches === true ||
|
|
399
412
|
options.progress === true ||
|
|
@@ -418,7 +431,6 @@ function runWorkflowCommand(args) {
|
|
|
418
431
|
sessionId,
|
|
419
432
|
startPrepared: args.prepared,
|
|
420
433
|
priorSessionId: optionalText(args.options.prior),
|
|
421
|
-
maxAttempts: attemptsOf(args.options),
|
|
422
434
|
maxDispatches: ceiling,
|
|
423
435
|
refreshWorkflow: args.options.refreshWorkflow === true,
|
|
424
436
|
...outputFlags(args.options),
|
|
@@ -432,7 +444,6 @@ function runWorkflowCommand(args) {
|
|
|
432
444
|
stageId,
|
|
433
445
|
startPrepared: args.prepared,
|
|
434
446
|
priorSessionId: optionalText(args.options.prior),
|
|
435
|
-
maxAttempts: null,
|
|
436
447
|
maxDispatches: null,
|
|
437
448
|
refreshWorkflow: args.options.refreshWorkflow === true,
|
|
438
449
|
...outputFlags(args.options),
|
|
@@ -443,6 +454,9 @@ function continueSession(session, options) {
|
|
|
443
454
|
printError("--stage is empty.");
|
|
444
455
|
return 2;
|
|
445
456
|
}
|
|
457
|
+
const workspace = findWorkspace(invocationCwdPath());
|
|
458
|
+
if (isAgentSession(workspace, session))
|
|
459
|
+
return continueRoleSession(workspace, session, options);
|
|
446
460
|
if (rejectOnce(options))
|
|
447
461
|
return 2;
|
|
448
462
|
const reply = readReply(options);
|
|
@@ -455,8 +469,8 @@ function continueSession(session, options) {
|
|
|
455
469
|
sessionId: session,
|
|
456
470
|
stageId,
|
|
457
471
|
priorSessionId: optionalText(options.prior),
|
|
458
|
-
maxAttempts: null,
|
|
459
472
|
maxDispatches: null,
|
|
473
|
+
continueFailed: true,
|
|
460
474
|
refreshWorkflow: options.refreshWorkflow === true,
|
|
461
475
|
reply,
|
|
462
476
|
...outputFlags(options),
|
|
@@ -469,13 +483,57 @@ function continueSession(session, options) {
|
|
|
469
483
|
action: "continue",
|
|
470
484
|
sessionId: session,
|
|
471
485
|
priorSessionId: optionalText(options.prior),
|
|
472
|
-
maxAttempts: attemptsOf(options),
|
|
473
486
|
maxDispatches: ceiling,
|
|
487
|
+
continueFailed: true,
|
|
474
488
|
refreshWorkflow: options.refreshWorkflow === true,
|
|
475
489
|
reply,
|
|
476
490
|
...outputFlags(options),
|
|
477
491
|
});
|
|
478
492
|
}
|
|
493
|
+
function isAgentSession(workspace, sessionId) {
|
|
494
|
+
try {
|
|
495
|
+
requireAgentSession(workspace, sessionId);
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
if (!(error instanceof ConfigurationError))
|
|
500
|
+
throw error;
|
|
501
|
+
if (error.message === `Session not found: ${sessionId}.`)
|
|
502
|
+
return false;
|
|
503
|
+
if (error.message === `Session ${sessionId} was not created by agent.`)
|
|
504
|
+
return false;
|
|
505
|
+
if (error.message.startsWith("Session status file is corrupt:"))
|
|
506
|
+
return false;
|
|
507
|
+
throw error;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function continueRoleSession(workspace, sessionId, options) {
|
|
511
|
+
const flags = [];
|
|
512
|
+
if (options.once === true)
|
|
513
|
+
flags.push("--once");
|
|
514
|
+
if (options.stage !== undefined)
|
|
515
|
+
flags.push("--stage");
|
|
516
|
+
if (options.prior !== undefined)
|
|
517
|
+
flags.push("--prior");
|
|
518
|
+
if (options.maxDispatches !== undefined)
|
|
519
|
+
flags.push("--max-dispatches");
|
|
520
|
+
if (options.unlimitedDispatches === true)
|
|
521
|
+
flags.push("--unlimited-dispatches");
|
|
522
|
+
if (options.progress === true)
|
|
523
|
+
flags.push("--progress");
|
|
524
|
+
if (options.verbose === true)
|
|
525
|
+
flags.push("--verbose");
|
|
526
|
+
if (options.refreshWorkflow === true)
|
|
527
|
+
flags.push("--refresh-workflow");
|
|
528
|
+
if (flags.length > 0) {
|
|
529
|
+
printError(`Session ${sessionId} is a role session. Omit ${flags.join(", ")}.`);
|
|
530
|
+
return 2;
|
|
531
|
+
}
|
|
532
|
+
const reply = readReply(options);
|
|
533
|
+
if (reply === "invalid")
|
|
534
|
+
return 2;
|
|
535
|
+
return continueAgent(workspace, { sessionId, reply });
|
|
536
|
+
}
|
|
479
537
|
function readReply(options) {
|
|
480
538
|
const hasReply = options.reply !== undefined;
|
|
481
539
|
const hasFile = options.replyFile !== undefined;
|
|
@@ -518,7 +576,6 @@ function addDispatchOptions(command, onceDescription) {
|
|
|
518
576
|
.option("--once", onceDescription)
|
|
519
577
|
.option("--stage <stage>", "Dispatch this ready stage once. It does not have to be the first ready stage.")
|
|
520
578
|
.option("--prior <session>", PRIOR_HELP)
|
|
521
|
-
.option("--attempts <count>", "Failed executions allowed for one stage. Default: 3.")
|
|
522
579
|
.option("--max-dispatches <count>", `Stage dispatches allowed in this command. Default: ${DEFAULT_MAX_DISPATCHES}. Pass --unlimited-dispatches to remove the ceiling.`)
|
|
523
580
|
.option("--unlimited-dispatches", "Remove the dispatch ceiling for this command.")
|
|
524
581
|
.option("--progress", PROGRESS_HELP)
|
|
@@ -526,7 +583,7 @@ function addDispatchOptions(command, onceDescription) {
|
|
|
526
583
|
.option("--refresh-workflow", "Use the current workflow, models, and role instructions for this session. Requires an existing session that has a frozen workflow.");
|
|
527
584
|
}
|
|
528
585
|
function outputFlags(options) {
|
|
529
|
-
return {
|
|
586
|
+
return { itemProgress: options.progress === true };
|
|
530
587
|
}
|
|
531
588
|
function showHelpWhenBare(status, command) {
|
|
532
589
|
command.action(() => {
|
|
@@ -590,10 +647,6 @@ function rejectOnce(options) {
|
|
|
590
647
|
if (options.once !== true && options.stage === undefined)
|
|
591
648
|
return false;
|
|
592
649
|
const flag = options.once === true ? "--once" : "--stage";
|
|
593
|
-
if (options.attempts !== undefined) {
|
|
594
|
-
printError(`${flag} cannot be combined with --attempts.`);
|
|
595
|
-
return true;
|
|
596
|
-
}
|
|
597
650
|
if (options.maxDispatches !== undefined) {
|
|
598
651
|
printError(`${flag} cannot be combined with --max-dispatches.`);
|
|
599
652
|
return true;
|
|
@@ -617,12 +670,6 @@ function dispatchCeiling(options) {
|
|
|
617
670
|
return DEFAULT_MAX_DISPATCHES;
|
|
618
671
|
return integerFlag(maxDispatches, "--max-dispatches");
|
|
619
672
|
}
|
|
620
|
-
function attemptsOf(options) {
|
|
621
|
-
const raw = optionalText(options.attempts);
|
|
622
|
-
if (raw === null)
|
|
623
|
-
return 3;
|
|
624
|
-
return integerFlag(raw, "--attempts");
|
|
625
|
-
}
|
|
626
673
|
function integerFlag(raw, flag) {
|
|
627
674
|
if (!/^[0-9]+$/.test(raw) || Number(raw) < 1) {
|
|
628
675
|
throw new ConfigurationError(`${flag} must be at least 1.`);
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { ModelCatalog } from "@nodeint/migent-adapters";
|
|
2
|
-
import { type
|
|
3
|
-
export interface
|
|
2
|
+
import { type Provider, type ProviderOption } from "@nodeint/migent-kernel";
|
|
3
|
+
export interface CatalogProvider extends Provider {
|
|
4
4
|
modelCatalogCommand(): string[];
|
|
5
|
+
/** Written to the catalog command's stdin when the CLI reads its request there. */
|
|
6
|
+
modelCatalogInput?(): string;
|
|
5
7
|
parseModelCatalog(stdout: string): ModelCatalog;
|
|
6
8
|
optionValuesCommand(name: string, model: string): string[] | null;
|
|
7
9
|
parseOptionValues(name: string, text: string, model?: string): readonly string[];
|
|
8
10
|
providerOptions(): readonly ProviderOption[];
|
|
9
11
|
}
|
|
10
12
|
export type Which = (command: string) => string | null;
|
|
11
|
-
export type RunCommand = (argv: string[]) => string | Promise<string>;
|
|
13
|
+
export type RunCommand = (argv: string[], input?: string) => string | Promise<string>;
|
|
12
14
|
export declare class ProviderCommandFailed extends Error {
|
|
13
15
|
}
|
|
14
|
-
export declare function
|
|
15
|
-
export declare function asCatalog(
|
|
16
|
-
export declare function
|
|
17
|
-
export declare function detected(
|
|
18
|
-
export declare function loadCatalog(
|
|
16
|
+
export declare function providerMap(): Record<string, Provider>;
|
|
17
|
+
export declare function asCatalog(provider: Provider): CatalogProvider;
|
|
18
|
+
export declare function requireProvider(providers: Record<string, Provider>, provider: string): Provider;
|
|
19
|
+
export declare function detected(providers: Record<string, Provider>, which: Which): Array<[string, string | null, Provider]>;
|
|
20
|
+
export declare function loadCatalog(provider: Provider, which: Which, runCommand: RunCommand): Promise<ModelCatalog>;
|
|
19
21
|
export declare function knownModel(model: string, catalog: ModelCatalog): string;
|
|
20
22
|
export declare function parseOptions(pairs: readonly string[]): Record<string, string>;
|
|
21
|
-
export declare function loadOptionValues(
|
|
22
|
-
export declare function acceptedOptions(
|
|
23
|
-
export declare function runProviderCommand(argv: string[]): string;
|
|
23
|
+
export declare function loadOptionValues(provider: Provider, name: string, model: string, catalog: ModelCatalog, which: Which, runCommand: RunCommand): Promise<readonly string[] | null>;
|
|
24
|
+
export declare function acceptedOptions(provider: Provider, model: string, catalog: ModelCatalog, which: Which, runCommand: RunCommand, options: Readonly<Record<string, string>>): Promise<Record<string, string>>;
|
|
25
|
+
export declare function runProviderCommand(argv: string[], input?: string): string;
|
package/dist/provider.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { ModelCatalog,
|
|
1
|
+
import { ModelCatalog, defaultProviders } from "@nodeint/migent-adapters";
|
|
2
2
|
import { spawnProviderSync } from "@nodeint/migent-kernel";
|
|
3
3
|
export class ProviderCommandFailed extends Error {
|
|
4
4
|
}
|
|
5
|
-
export function
|
|
6
|
-
return
|
|
5
|
+
export function providerMap() {
|
|
6
|
+
return defaultProviders();
|
|
7
7
|
}
|
|
8
|
-
export function asCatalog(
|
|
9
|
-
const candidate =
|
|
8
|
+
export function asCatalog(provider) {
|
|
9
|
+
const candidate = provider;
|
|
10
10
|
if (typeof candidate.modelCatalogCommand !== "function" || typeof candidate.parseModelCatalog !== "function") {
|
|
11
|
-
throw new ProviderCommandFailed(`${
|
|
11
|
+
throw new ProviderCommandFailed(`${provider.command} does not list models.`);
|
|
12
12
|
}
|
|
13
13
|
return candidate;
|
|
14
14
|
}
|
|
15
|
-
export function
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
const supported = Object.keys(
|
|
15
|
+
export function requireProvider(providers, provider) {
|
|
16
|
+
const impl = providers[provider];
|
|
17
|
+
if (impl === undefined) {
|
|
18
|
+
const supported = Object.keys(providers).sort().join(", ");
|
|
19
19
|
throw new ProviderCommandFailed(`Unknown provider: ${provider}. Supported: ${supported}.`);
|
|
20
20
|
}
|
|
21
|
-
return
|
|
21
|
+
return impl;
|
|
22
22
|
}
|
|
23
|
-
export function detected(
|
|
24
|
-
const rows = Object.entries(
|
|
23
|
+
export function detected(providers, which) {
|
|
24
|
+
const rows = Object.entries(providers).filter(([, provider]) => provider.requiresClient !== true).map(([name, provider]) => [name, which(provider.command), provider]);
|
|
25
25
|
rows.sort((left, right) => {
|
|
26
26
|
const leftMissing = left[1] === null ? 1 : 0;
|
|
27
27
|
const rightMissing = right[1] === null ? 1 : 0;
|
|
@@ -35,24 +35,24 @@ export function detected(adapters, which) {
|
|
|
35
35
|
});
|
|
36
36
|
return rows;
|
|
37
37
|
}
|
|
38
|
-
export async function loadCatalog(
|
|
39
|
-
if (which(
|
|
40
|
-
throw new ProviderCommandFailed(`${
|
|
38
|
+
export async function loadCatalog(provider, which, runCommand) {
|
|
39
|
+
if (which(provider.command) === null) {
|
|
40
|
+
throw new ProviderCommandFailed(`${provider.command} is not on PATH, so its models could not be listed.`);
|
|
41
41
|
}
|
|
42
|
-
const
|
|
42
|
+
const catalogProvider = asCatalog(provider);
|
|
43
43
|
let stdout;
|
|
44
44
|
try {
|
|
45
|
-
stdout = await runCommand(
|
|
45
|
+
stdout = await runCommand(catalogProvider.modelCatalogCommand(), catalogProvider.modelCatalogInput?.());
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
48
48
|
if (error instanceof ProviderCommandFailed)
|
|
49
49
|
throw error;
|
|
50
50
|
const reason = error instanceof Error ? error.message : String(error);
|
|
51
|
-
throw new ProviderCommandFailed(`Could not list ${
|
|
51
|
+
throw new ProviderCommandFailed(`Could not list ${provider.command} models: ${reason}`);
|
|
52
52
|
}
|
|
53
|
-
const catalog =
|
|
53
|
+
const catalog = catalogProvider.parseModelCatalog(stdout);
|
|
54
54
|
if (catalog.ids.length === 0)
|
|
55
|
-
throw new ProviderCommandFailed(`${
|
|
55
|
+
throw new ProviderCommandFailed(`${provider.command} returned no models.`);
|
|
56
56
|
return catalog;
|
|
57
57
|
}
|
|
58
58
|
export function knownModel(model, catalog) {
|
|
@@ -75,16 +75,16 @@ export function parseOptions(pairs) {
|
|
|
75
75
|
}
|
|
76
76
|
return parsed;
|
|
77
77
|
}
|
|
78
|
-
export async function loadOptionValues(
|
|
78
|
+
export async function loadOptionValues(provider, name, model, catalog, which, runCommand) {
|
|
79
79
|
const found = catalog.valuesFor(model, name);
|
|
80
80
|
if (found !== null)
|
|
81
81
|
return found;
|
|
82
|
-
const
|
|
83
|
-
const argv =
|
|
82
|
+
const catalogProvider = asCatalog(provider);
|
|
83
|
+
const argv = catalogProvider.optionValuesCommand(name, model);
|
|
84
84
|
if (argv === null)
|
|
85
85
|
return null;
|
|
86
|
-
if (which(
|
|
87
|
-
throw new ProviderCommandFailed(`${
|
|
86
|
+
if (which(provider.command) === null) {
|
|
87
|
+
throw new ProviderCommandFailed(`${provider.command} is not on PATH, so its ${name} values could not be listed.`);
|
|
88
88
|
}
|
|
89
89
|
let text;
|
|
90
90
|
try {
|
|
@@ -95,18 +95,18 @@ export async function loadOptionValues(adapter, name, model, catalog, which, run
|
|
|
95
95
|
text = error.message;
|
|
96
96
|
else {
|
|
97
97
|
const reason = error instanceof Error ? error.message : String(error);
|
|
98
|
-
throw new ProviderCommandFailed(`Could not list ${
|
|
98
|
+
throw new ProviderCommandFailed(`Could not list ${provider.command} ${name} values for ${model}: ${reason}`);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
return
|
|
101
|
+
return catalogProvider.parseOptionValues(name, text, model);
|
|
102
102
|
}
|
|
103
|
-
export async function acceptedOptions(
|
|
103
|
+
export async function acceptedOptions(provider, model, catalog, which, runCommand, options) {
|
|
104
104
|
if (Object.keys(options).length === 0)
|
|
105
105
|
return {};
|
|
106
|
-
|
|
106
|
+
provider.validateOptions(options);
|
|
107
107
|
const stored = {};
|
|
108
108
|
for (const [name, value] of Object.entries(options)) {
|
|
109
|
-
const values = await loadOptionValues(
|
|
109
|
+
const values = await loadOptionValues(provider, name, model, catalog, which, runCommand);
|
|
110
110
|
if (values === null) {
|
|
111
111
|
stored[name] = value;
|
|
112
112
|
continue;
|
|
@@ -120,11 +120,16 @@ export async function acceptedOptions(adapter, model, catalog, which, runCommand
|
|
|
120
120
|
}
|
|
121
121
|
return stored;
|
|
122
122
|
}
|
|
123
|
-
export function runProviderCommand(argv) {
|
|
123
|
+
export function runProviderCommand(argv, input) {
|
|
124
124
|
const [command, ...args] = argv;
|
|
125
125
|
if (command === undefined)
|
|
126
126
|
throw new ProviderCommandFailed("Provider command is empty.");
|
|
127
|
-
const completed = spawnProviderSync(command, args, {
|
|
127
|
+
const completed = spawnProviderSync(command, args, {
|
|
128
|
+
encoding: "utf8",
|
|
129
|
+
timeout: 60_000,
|
|
130
|
+
windowsHide: true,
|
|
131
|
+
...(input === undefined ? {} : { input }),
|
|
132
|
+
});
|
|
128
133
|
if (completed.error) {
|
|
129
134
|
throw new Error(completed.error.message);
|
|
130
135
|
}
|
package/dist/session-row.d.ts
CHANGED
|
@@ -7,9 +7,11 @@ export declare const SESSION_COLUMN: {
|
|
|
7
7
|
readonly decision: 5;
|
|
8
8
|
readonly outputs: 6;
|
|
9
9
|
readonly task: 7;
|
|
10
|
+
readonly title: 8;
|
|
10
11
|
};
|
|
11
12
|
export interface SessionFields {
|
|
12
13
|
session: string;
|
|
14
|
+
title: string;
|
|
13
15
|
workflow: string;
|
|
14
16
|
status: string;
|
|
15
17
|
execution: string;
|
package/dist/session-row.js
CHANGED
|
@@ -7,10 +7,12 @@ export const SESSION_COLUMN = {
|
|
|
7
7
|
decision: 5,
|
|
8
8
|
outputs: 6,
|
|
9
9
|
task: 7,
|
|
10
|
+
title: 8,
|
|
10
11
|
};
|
|
11
12
|
export function sessionFields(row) {
|
|
12
13
|
return {
|
|
13
14
|
session: row[SESSION_COLUMN.session] ?? "",
|
|
15
|
+
title: row[SESSION_COLUMN.title] ?? "",
|
|
14
16
|
workflow: row[SESSION_COLUMN.workflow] ?? "",
|
|
15
17
|
status: row[SESSION_COLUMN.status] ?? "",
|
|
16
18
|
execution: row[SESSION_COLUMN.execution] ?? "",
|