@aiwg/cli 2026.8.8 → 2026.8.10
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 +23 -8
- package/THIRD_PARTY_NOTICES.md +35 -0
- package/agentic/code/providers/capability-matrix.yaml +3 -3
- package/bin/aiwg.mjs +125 -0
- package/dist/src/artifacts/backends/graphology-backend.js +4 -3
- package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
- package/dist/src/artifacts/cli.js +2 -2
- package/dist/src/artifacts/corpus-tools/cli.js +27 -0
- package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
- package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
- package/dist/src/artifacts/discover-facets.js +2 -2
- package/dist/src/artifacts/embedding-index.js +9 -8
- package/dist/src/artifacts/graph-backend.js +2 -2
- package/dist/src/artifacts/query-engine.js +21 -7
- package/dist/src/artifacts/repair.js +47 -0
- package/dist/src/artifacts/types.js +3 -3
- package/dist/src/cli/command-log.js +2 -2
- package/dist/src/cli/handlers/artifacts.js +50 -1
- package/dist/src/cli/handlers/cost-report.js +71 -0
- package/dist/src/cli/handlers/evidence.js +78 -0
- package/dist/src/cli/handlers/help.js +9 -0
- package/dist/src/cli/handlers/index.js +8 -3
- package/dist/src/cli/handlers/local-executor.js +4 -3
- package/dist/src/cli/handlers/refresh.js +20 -8
- package/dist/src/cli/handlers/regenerate.js +3 -3
- package/dist/src/cli/handlers/serve.js +15 -36
- package/dist/src/cli/handlers/setup-manifest.js +8 -1
- package/dist/src/cli/handlers/use.js +256 -70
- package/dist/src/cli/handlers/utilities.js +149 -0
- package/dist/src/cli/handlers/workspace.js +10 -0
- package/dist/src/cli/help-generator.js +2 -1
- package/dist/src/cli/router.js +4 -1
- package/dist/src/cli/services/deployment-verification.js +596 -0
- package/dist/src/cli/skill-usage.js +2 -2
- package/dist/src/cli/workflow-orchestrator.js +1 -1
- package/dist/src/cli/workspace-signals.js +2 -2
- package/dist/src/config/aiwg-config.js +54 -27
- package/dist/src/config/cli.js +3 -3
- package/dist/src/config/project-artifacts-health.js +2 -0
- package/dist/src/config/project-artifacts-health.mjs +123 -0
- package/dist/src/config/project-artifacts-runtime.mjs +16 -0
- package/dist/src/config/project-artifacts.js +2 -1
- package/dist/src/cost/fleet-report.js +329 -0
- package/dist/src/evidence/bundle.js +256 -0
- package/dist/src/extensions/commands/definitions.js +77 -25
- package/dist/src/extensions/deployment-registration.js +6 -4
- package/dist/src/features/catalog.js +26 -0
- package/dist/src/features/cli.js +1 -3
- package/dist/src/features/runtime.js +17 -1
- package/dist/src/issues/cli.js +91 -7
- package/dist/src/mcp/server.mjs +1 -1
- package/dist/src/ops/registry.js +2 -2
- package/dist/src/policy/authorization.js +2 -2
- package/dist/src/providers/capability-matrix.yaml +3 -3
- package/dist/src/providers/provider-definitions.js +7 -5
- package/dist/src/providers/provider-definitions.mjs +1 -1
- package/dist/src/serve/pty-bridge.js +2 -8
- package/dist/src/serve/screen-reader.js +3 -6
- package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
- package/dist/src/smiths/context-pipeline/finalization.js +18 -5
- package/dist/src/smiths/context-pipeline/generator.js +2 -2
- package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
- package/package.json +2 -1
- package/tools/agents/deploy-agents.mjs +10 -11
- package/tools/agents/providers/base.mjs +47 -5
- package/tools/agents/providers/openclaw.mjs +5 -2
- package/tools/agents/providers/windsurf.mjs +13 -24
- package/tools/skills/deploy-skills-codex.mjs +21 -5
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { getProjectDir } from '../../config/aiwg-config.js';
|
|
2
2
|
import { moveProjectArtifacts } from '../../artifacts/move.js';
|
|
3
|
+
import { repairProjectArtifacts } from '../../artifacts/repair.js';
|
|
4
|
+
import { resolveProjectAiwgDir } from '../../config/project-artifacts.js';
|
|
3
5
|
function usage() {
|
|
4
6
|
return [
|
|
5
7
|
'aiwg artifacts — Manage the project AIWG artifact root',
|
|
6
8
|
'',
|
|
7
9
|
'Usage:',
|
|
10
|
+
' aiwg artifacts path [--json]',
|
|
8
11
|
' aiwg artifacts move --to <path> [--from <path>] [--dry-run] [--no-reindex] [--no-sync]',
|
|
9
12
|
' aiwg artifacts attach --to <existing-path> [--dry-run] [--no-reindex] [--no-sync]',
|
|
13
|
+
' aiwg artifacts repair --dry-run',
|
|
14
|
+
' aiwg artifacts repair --apply',
|
|
10
15
|
'',
|
|
11
16
|
'Notes:',
|
|
12
17
|
' move relocates a local artifact root; attach adopts an existing populated root.',
|
|
@@ -32,9 +37,53 @@ export const artifactsHandler = {
|
|
|
32
37
|
if (action === 'help' || ctx.args.includes('--help') || ctx.args.includes('-h')) {
|
|
33
38
|
return { exitCode: 0, message: usage() };
|
|
34
39
|
}
|
|
35
|
-
if (action !== 'move' && action !== 'attach') {
|
|
40
|
+
if (action !== 'path' && action !== 'move' && action !== 'attach' && action !== 'repair') {
|
|
36
41
|
return { exitCode: 1, message: `Unknown artifacts action: ${action}\n\n${usage()}` };
|
|
37
42
|
}
|
|
43
|
+
if (action === 'path') {
|
|
44
|
+
const projectDir = getProjectDir(ctx, ctx.args);
|
|
45
|
+
const artifactRoot = resolveProjectAiwgDir(projectDir);
|
|
46
|
+
if (ctx.args.includes('--json')) {
|
|
47
|
+
return {
|
|
48
|
+
exitCode: 0,
|
|
49
|
+
message: JSON.stringify({
|
|
50
|
+
schema: 'aiwg.artifacts.path.v1',
|
|
51
|
+
project_root: projectDir,
|
|
52
|
+
artifact_root: artifactRoot,
|
|
53
|
+
}, null, 2),
|
|
54
|
+
rawOutput: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { exitCode: 0, message: artifactRoot, rawOutput: true };
|
|
58
|
+
}
|
|
59
|
+
if (action === 'repair') {
|
|
60
|
+
try {
|
|
61
|
+
const applied = ctx.args.includes('--apply');
|
|
62
|
+
const result = await repairProjectArtifacts({
|
|
63
|
+
projectDir: getProjectDir(ctx, ctx.args),
|
|
64
|
+
apply: applied,
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
exitCode: 0,
|
|
68
|
+
message: [
|
|
69
|
+
`${applied ? 'Repaired' : 'Artifact repair dry run for'} ${result.before.classification}`,
|
|
70
|
+
` Local control plane: ${result.before.local_control_root}`,
|
|
71
|
+
` External corpus: ${result.before.artifact_root}`,
|
|
72
|
+
` Copy locally: ${result.copied.length ? result.copied.join(', ') : 'none'}`,
|
|
73
|
+
` Remove local identical corpus copies: ${result.removed.length ? result.removed.join(', ') : 'none'}`,
|
|
74
|
+
` Result: ${result.after.classification}`,
|
|
75
|
+
applied ? '' : 'No files changed. Re-run with --apply after reviewing this plan.',
|
|
76
|
+
].filter(Boolean).join('\n'),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return {
|
|
81
|
+
exitCode: 1,
|
|
82
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
83
|
+
message: `Artifact repair failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
38
87
|
const to = valueAfter(ctx.args, '--to');
|
|
39
88
|
if (!to) {
|
|
40
89
|
return { exitCode: 1, message: `Error: --to <path> is required.\n\n${usage()}` };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** OpenRouter fleet cost-report CLI handler. @issue #1187 */
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { FleetConfigMissingError, formatFleetSpendReport, generateFleetSpendReport, } from '../../cost/fleet-report.js';
|
|
5
|
+
function option(args, name) {
|
|
6
|
+
const index = args.indexOf(name);
|
|
7
|
+
const value = index >= 0 ? args[index + 1] : undefined;
|
|
8
|
+
return value && !value.startsWith('-') ? value : undefined;
|
|
9
|
+
}
|
|
10
|
+
function usage() {
|
|
11
|
+
return [
|
|
12
|
+
'Usage: aiwg cost-report (--fleet | --key <key_ref>) [--source openrouter] [--config <fleet.yaml>] [--json]',
|
|
13
|
+
'',
|
|
14
|
+
'Reads bot-to-key references from ~/.config/aiwg/fleet.yaml and credentials from',
|
|
15
|
+
'~/.config/aiwg/keys/<key_ref> or AIWG_OPENROUTER_KEY_<KEY_REF>.',
|
|
16
|
+
'',
|
|
17
|
+
'AIWG observes and correlates spend; OpenRouter enforces all key limits and caps.',
|
|
18
|
+
].join('\n');
|
|
19
|
+
}
|
|
20
|
+
export const costReportHandler = {
|
|
21
|
+
id: 'cost-report',
|
|
22
|
+
name: 'Cost Report',
|
|
23
|
+
description: 'Observe OpenRouter fleet spend and correlate it with local AIWG activity',
|
|
24
|
+
category: 'utility',
|
|
25
|
+
aliases: [],
|
|
26
|
+
async execute(ctx) {
|
|
27
|
+
if (ctx.args.includes('--help') || ctx.args.includes('-h'))
|
|
28
|
+
return { exitCode: 0, message: usage() };
|
|
29
|
+
const keyRef = option(ctx.args, '--key');
|
|
30
|
+
if (ctx.args.includes('--fleet') && keyRef)
|
|
31
|
+
return { exitCode: 2, message: 'Choose either --fleet or --key, not both.' };
|
|
32
|
+
if (!ctx.args.includes('--fleet') && !keyRef) {
|
|
33
|
+
return { exitCode: 2, message: `Select --fleet or --key <key_ref>.\n\n${usage()}` };
|
|
34
|
+
}
|
|
35
|
+
const source = option(ctx.args, '--source') ?? 'openrouter';
|
|
36
|
+
if (source !== 'openrouter')
|
|
37
|
+
return { exitCode: 2, message: `Unsupported source '${source}'. Only openrouter is supported.` };
|
|
38
|
+
const configuredPath = option(ctx.args, '--config');
|
|
39
|
+
const configPath = configuredPath
|
|
40
|
+
? path.resolve(ctx.cwd, configuredPath)
|
|
41
|
+
: path.join(os.homedir(), '.config', 'aiwg', 'fleet.yaml');
|
|
42
|
+
try {
|
|
43
|
+
const capValue = option(ctx.args, '--monthly-cap');
|
|
44
|
+
const cap = capValue === undefined ? 0 : Number(capValue);
|
|
45
|
+
if (!Number.isFinite(cap) || cap < 0)
|
|
46
|
+
return { exitCode: 2, message: '--monthly-cap must be a non-negative number.' };
|
|
47
|
+
const report = await generateFleetSpendReport({
|
|
48
|
+
cwd: ctx.cwd,
|
|
49
|
+
configPath,
|
|
50
|
+
signal: ctx.signal,
|
|
51
|
+
...(keyRef ? {
|
|
52
|
+
fleet: [{
|
|
53
|
+
bot: option(ctx.args, '--bot') ?? keyRef,
|
|
54
|
+
machine: option(ctx.args, '--machine') ?? os.hostname(),
|
|
55
|
+
key_ref: keyRef,
|
|
56
|
+
monthly_cap: cap,
|
|
57
|
+
}],
|
|
58
|
+
} : {}),
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
exitCode: report.bots.some(bot => bot.error) ? 1 : 0,
|
|
62
|
+
message: ctx.args.includes('--json') ? JSON.stringify(report, null, 2) : formatFleetSpendReport(report),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const message = error instanceof FleetConfigMissingError ? error.message : `Fleet cost report failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
67
|
+
return { exitCode: 1, message };
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=cost-report.js.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Evidence bundle export and verification CLI. @issue #2039 */
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createEvidenceBundle, verifyEvidenceBundle } from '../../evidence/bundle.js';
|
|
4
|
+
function values(args, flag) {
|
|
5
|
+
const result = [];
|
|
6
|
+
for (let index = 0; index < args.length; index++)
|
|
7
|
+
if (args[index] === flag && args[index + 1])
|
|
8
|
+
result.push(args[++index]);
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
11
|
+
function value(args, flag) { return values(args, flag)[0]; }
|
|
12
|
+
function versions(items) {
|
|
13
|
+
return Object.fromEntries(items.map(item => {
|
|
14
|
+
const index = item.indexOf('=');
|
|
15
|
+
if (index < 1 || index === item.length - 1)
|
|
16
|
+
throw new Error(`version '${item}' must use name=value`);
|
|
17
|
+
return [item.slice(0, index), item.slice(index + 1)];
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
function usage() {
|
|
21
|
+
return [
|
|
22
|
+
'Usage:',
|
|
23
|
+
' aiwg evidence export --output <dir> [--activity-export <json>] [--report <file>] [--source <file>]',
|
|
24
|
+
' [--eval-config <file>] [--provenance <file>] [--model-version name=value] [--tool-version name=value]',
|
|
25
|
+
' [--check-only --not-run <reason>] [--json]',
|
|
26
|
+
' aiwg evidence verify <bundle> [--expected-root <sha256>] [--json]',
|
|
27
|
+
].join('\n');
|
|
28
|
+
}
|
|
29
|
+
export const evidenceHandler = {
|
|
30
|
+
id: 'evidence', name: 'Evidence', description: 'Export and verify portable evaluation evidence bundles', category: 'utility', aliases: [],
|
|
31
|
+
async execute(ctx) {
|
|
32
|
+
const [subcommand] = ctx.args;
|
|
33
|
+
if (!subcommand || subcommand === '--help' || subcommand === '-h')
|
|
34
|
+
return { exitCode: 0, message: usage() };
|
|
35
|
+
try {
|
|
36
|
+
if (subcommand === 'verify') {
|
|
37
|
+
const bundle = ctx.args[1];
|
|
38
|
+
if (!bundle || bundle.startsWith('-'))
|
|
39
|
+
return { exitCode: 2, message: usage() };
|
|
40
|
+
const expectedRoot = value(ctx.args, '--expected-root');
|
|
41
|
+
if (expectedRoot && !/^[0-9a-f]{64}$/i.test(expectedRoot))
|
|
42
|
+
return { exitCode: 2, message: '--expected-root must be a SHA-256 hex digest' };
|
|
43
|
+
const result = await verifyEvidenceBundle(path.resolve(ctx.cwd, bundle), expectedRoot?.toLowerCase());
|
|
44
|
+
return { exitCode: result.valid ? 0 : 1, message: ctx.args.includes('--json') ? JSON.stringify(result, null, 2) : [
|
|
45
|
+
`Evidence bundle: ${result.valid ? 'VALID' : 'INVALID'} (${result.status})`,
|
|
46
|
+
...result.errors.map(error => `ERROR: ${error}`), ...result.warnings.map(warning => `WARN: ${warning}`),
|
|
47
|
+
].join('\n') };
|
|
48
|
+
}
|
|
49
|
+
if (subcommand !== 'export')
|
|
50
|
+
return { exitCode: 2, message: usage() };
|
|
51
|
+
const output = value(ctx.args, '--output');
|
|
52
|
+
if (!output)
|
|
53
|
+
return { exitCode: 2, message: '--output is required.\n\n' + usage() };
|
|
54
|
+
const checkOnly = ctx.args.includes('--check-only');
|
|
55
|
+
const notRunReason = value(ctx.args, '--not-run');
|
|
56
|
+
if (checkOnly !== Boolean(notRunReason))
|
|
57
|
+
return { exitCode: 2, message: '--check-only and --not-run <reason> must be used together' };
|
|
58
|
+
const inputs = [
|
|
59
|
+
...values(ctx.args, '--activity-export').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'activity-export' })),
|
|
60
|
+
...values(ctx.args, '--report').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'report' })),
|
|
61
|
+
...values(ctx.args, '--source').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'source' })),
|
|
62
|
+
...values(ctx.args, '--eval-config').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'eval-config' })),
|
|
63
|
+
...values(ctx.args, '--provenance').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'provenance' })),
|
|
64
|
+
];
|
|
65
|
+
const manifest = await createEvidenceBundle({
|
|
66
|
+
output: path.resolve(ctx.cwd, output), inputs,
|
|
67
|
+
modelVersions: versions(values(ctx.args, '--model-version')),
|
|
68
|
+
toolVersions: { aiwg: process.env.npm_package_version ?? 'unknown', node: process.version, ...versions(values(ctx.args, '--tool-version')) },
|
|
69
|
+
checkOnly, notRunReason,
|
|
70
|
+
});
|
|
71
|
+
return { exitCode: 0, message: ctx.args.includes('--json') ? JSON.stringify(manifest, null, 2) : `Evidence bundle ${manifest.status}: ${path.resolve(ctx.cwd, output)}\nVerifier root: ${manifest.verifier.root}` };
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return { exitCode: 1, message: `Evidence command failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -95,6 +95,15 @@ function displayHelp() {
|
|
|
95
95
|
]);
|
|
96
96
|
helpGroup('VALIDATION', [
|
|
97
97
|
['validate-metadata [path]', 'Validate AIWG component metadata (defaults to agentic/code)'],
|
|
98
|
+
['context-firewall [scan]', 'Audit provider context, trust, drift, poisoning signals, and budget'],
|
|
99
|
+
['context-firewall baseline', 'Plan or explicitly write the reviewed context baseline'],
|
|
100
|
+
]);
|
|
101
|
+
helpGroup('METRICS', [
|
|
102
|
+
['cost-report --fleet', 'Observe OpenRouter per-bot MTD spend and correlate local activity'],
|
|
103
|
+
]);
|
|
104
|
+
helpGroup('EVIDENCE', [
|
|
105
|
+
['evidence export --output <dir>', 'Package portable activity, report, source, eval, and provenance evidence'],
|
|
106
|
+
['evidence verify <bundle>', 'Verify every member hash and the bundle checkpoint'],
|
|
98
107
|
]);
|
|
99
108
|
helpGroup('SCAFFOLDING', [
|
|
100
109
|
['new-bundle <name>', 'Create project-local bundle (--type extension|addon|framework|plugin|provider, --starter skill|rule|agent|minimal, --dry-run)'],
|
|
@@ -16,7 +16,7 @@ import { versionHandler } from './version.js';
|
|
|
16
16
|
import { authHandler } from './auth.js';
|
|
17
17
|
import { useHandler } from './use.js';
|
|
18
18
|
import { statusHandler, wizardHandler, migrateWorkspaceHandler, rollbackWorkspaceHandler, workspaceHandlers, } from './workspace.js';
|
|
19
|
-
import { prefillCardsHandler, contributeStartHandler, validateMetadataHandler, doctorHandler, updateHandler, utilityHandlers, } from './utilities.js';
|
|
19
|
+
import { prefillCardsHandler, contributeStartHandler, validateMetadataHandler, doctorHandler, contextFirewallHandler, updateHandler, utilityHandlers, } from './utilities.js';
|
|
20
20
|
import { skillLintHandler } from './skill-lint.js';
|
|
21
21
|
import { addAgentHandler, addCommandHandler, addSkillHandler, addBehaviorHandler, addTemplateHandler, scaffoldAddonHandler, scaffoldExtensionHandler, scaffoldFrameworkHandler, scaffoldingHandlers, } from './scaffolding.js';
|
|
22
22
|
import { behaviorHandler, daemonInitHandler, daemonHandlers, } from './daemon.js';
|
|
@@ -59,12 +59,14 @@ import { skillUsageHandler } from './skill-usage.js';
|
|
|
59
59
|
import { modelsHandler } from './models.js';
|
|
60
60
|
import { versionsHandler } from './resource-versions.js';
|
|
61
61
|
import { jobHandler } from './job.js';
|
|
62
|
+
import { costReportHandler } from './cost-report.js';
|
|
63
|
+
import { evidenceHandler } from './evidence.js';
|
|
62
64
|
// Re-export individual handlers
|
|
63
65
|
export {
|
|
64
66
|
// Maintenance
|
|
65
|
-
helpHandler, versionHandler, authHandler, doctorHandler, updateHandler, refreshHandler, regenerateHandler, workspaceContextHandler,
|
|
67
|
+
helpHandler, versionHandler, authHandler, doctorHandler, contextFirewallHandler, updateHandler, refreshHandler, regenerateHandler, workspaceContextHandler,
|
|
66
68
|
// Framework management
|
|
67
|
-
useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler,
|
|
69
|
+
useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler,
|
|
68
70
|
// Project
|
|
69
71
|
newBundleHandler, quickrefHandler, newProjectHandler, sessionHandler, sessionsHandler,
|
|
70
72
|
// Workspace
|
|
@@ -118,6 +120,7 @@ export const allHandlers = [
|
|
|
118
120
|
versionHandler,
|
|
119
121
|
authHandler,
|
|
120
122
|
doctorHandler,
|
|
123
|
+
contextFirewallHandler,
|
|
121
124
|
updateHandler,
|
|
122
125
|
refreshHandler,
|
|
123
126
|
regenerateHandler,
|
|
@@ -143,6 +146,8 @@ export const allHandlers = [
|
|
|
143
146
|
issueAuditHandler,
|
|
144
147
|
runHandler,
|
|
145
148
|
jobHandler,
|
|
149
|
+
costReportHandler,
|
|
150
|
+
evidenceHandler,
|
|
146
151
|
// Workspace management
|
|
147
152
|
...workspaceHandlers,
|
|
148
153
|
// Subcommand handlers (MCP, catalog, index, skills)
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import path from 'path';
|
|
20
20
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
21
21
|
import { randomUUID } from 'crypto';
|
|
22
|
+
import { pathToFileURL } from 'node:url';
|
|
22
23
|
import { projectAiwgPath } from '../../config/project-artifacts.js';
|
|
23
24
|
const DEFAULT_PORT = 8200;
|
|
24
25
|
const DEFAULT_BIND = '127.0.0.1';
|
|
@@ -131,7 +132,7 @@ export const localExecutorServeHandler = {
|
|
|
131
132
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
132
133
|
let DaemonSupervisor;
|
|
133
134
|
try {
|
|
134
|
-
const shimMod = await
|
|
135
|
+
const shimMod = await import(pathToFileURL(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'executor-shim.mjs')).href);
|
|
135
136
|
ExecutorShim = shimMod.ExecutorShim;
|
|
136
137
|
startExecutorServer = shimMod.startExecutorServer;
|
|
137
138
|
}
|
|
@@ -142,7 +143,7 @@ export const localExecutorServeHandler = {
|
|
|
142
143
|
};
|
|
143
144
|
}
|
|
144
145
|
try {
|
|
145
|
-
const dsMod = await
|
|
146
|
+
const dsMod = await import(pathToFileURL(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'daemon-supervisor.mjs')).href);
|
|
146
147
|
DaemonSupervisor = dsMod.DaemonSupervisor;
|
|
147
148
|
}
|
|
148
149
|
catch (err) {
|
|
@@ -155,7 +156,7 @@ export const localExecutorServeHandler = {
|
|
|
155
156
|
// requires it. Try loading from ralph-external/orchestrator.mjs.
|
|
156
157
|
let agentSupervisorInstance = null;
|
|
157
158
|
try {
|
|
158
|
-
const orchMod = await
|
|
159
|
+
const orchMod = await import(pathToFileURL(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'orchestrator.mjs')).href);
|
|
159
160
|
const OrchClass = orchMod.Orchestrator ?? orchMod.default;
|
|
160
161
|
if (OrchClass) {
|
|
161
162
|
const orch = new OrchClass({ maxConcurrent: opts.maxConcurrency });
|
|
@@ -149,7 +149,7 @@ export function collectModelDeployArgs(args) {
|
|
|
149
149
|
export const refreshHandler = {
|
|
150
150
|
id: 'refresh',
|
|
151
151
|
name: 'Refresh',
|
|
152
|
-
description: 'Refresh AIWG to latest version and re-deploy
|
|
152
|
+
description: 'Refresh AIWG to latest version and re-deploy installed frameworks',
|
|
153
153
|
category: 'maintenance',
|
|
154
154
|
aliases: ['--refresh', 'sync', '--sync'],
|
|
155
155
|
async execute(ctx) {
|
|
@@ -241,13 +241,24 @@ export const refreshHandler = {
|
|
|
241
241
|
if (!quiet)
|
|
242
242
|
ui.dim(' Skipping package update (--skip-update)');
|
|
243
243
|
}
|
|
244
|
-
// Step 4: Re-deploy frameworks
|
|
245
|
-
|
|
244
|
+
// Step 4: Re-deploy frameworks. Both the default form and --all mean
|
|
245
|
+
// "all installed", never the `aiwg use all` expansion meta-target. This
|
|
246
|
+
// preserves the operator's selected footprint and removal symmetry.
|
|
247
|
+
const refreshConfig = await readAiwgConfig(ctx.cwd);
|
|
248
|
+
const installedFrameworks = Object.keys(refreshConfig?.installed ?? {});
|
|
249
|
+
const requestedFrameworks = frameworksArg
|
|
250
|
+
? frameworksArg.split(',').map(item => item.trim()).filter(Boolean)
|
|
251
|
+
: [];
|
|
252
|
+
const frameworks = !frameworksArg || requestedFrameworks.includes('all')
|
|
253
|
+
? installedFrameworks
|
|
254
|
+
: requestedFrameworks;
|
|
246
255
|
if (!quiet)
|
|
247
256
|
ui.info(dryRun ? 'Would re-deploy frameworks...' : 'Re-deploying frameworks...');
|
|
248
257
|
if (!dryRun) {
|
|
249
|
-
|
|
250
|
-
|
|
258
|
+
if (frameworks.length === 0 && !quiet) {
|
|
259
|
+
ui.dim(' No installed frameworks or addons to re-deploy');
|
|
260
|
+
}
|
|
261
|
+
for (const fw of frameworks) {
|
|
251
262
|
const providerArgs = ['--provider', detectedProvider, ...modelDeployArgs];
|
|
252
263
|
const useResult = await runner.run('tools/cli/deploy.mjs', [fw, ...providerArgs], { capture: quiet });
|
|
253
264
|
if (useResult.exitCode === 0) {
|
|
@@ -261,9 +272,10 @@ export const refreshHandler = {
|
|
|
261
272
|
}
|
|
262
273
|
}
|
|
263
274
|
else {
|
|
264
|
-
const targets = frameworks || ['all installed frameworks'];
|
|
265
275
|
if (!quiet) {
|
|
266
|
-
|
|
276
|
+
if (frameworks.length === 0)
|
|
277
|
+
ui.dim(' No installed frameworks or addons');
|
|
278
|
+
for (const fw of frameworks) {
|
|
267
279
|
ui.dim(` Would re-deploy: ${fw}`);
|
|
268
280
|
}
|
|
269
281
|
}
|
|
@@ -419,7 +431,7 @@ export const refreshHandler = {
|
|
|
419
431
|
const output = JSON.stringify({
|
|
420
432
|
status: dryRun ? 'dry-run' : 'refreshed',
|
|
421
433
|
provider: detectedProvider,
|
|
422
|
-
frameworks
|
|
434
|
+
frameworks,
|
|
423
435
|
skipUpdate,
|
|
424
436
|
channel: channel || undefined,
|
|
425
437
|
staleAgentRemovals,
|
|
@@ -25,7 +25,7 @@ import * as ui from '../ui.js';
|
|
|
25
25
|
import { generate as generateContextFiles, discoverDeployedArtifacts, shouldEmitContextFiles, buildNormalizedAiwgMd, writeNormalizedAiwgMd, injectLegacyContext, migrateWorkspaceContext, extractExistingProjectContext, } from '../../smiths/context-pipeline/index.js';
|
|
26
26
|
import { resolveActiveProvider } from '../provider-resolution.js';
|
|
27
27
|
import { getProviderContextDiscoveryPathStrings } from '../../providers/provider-definitions.js';
|
|
28
|
-
import {
|
|
28
|
+
import { projectControlPath } from '../../config/project-artifacts.js';
|
|
29
29
|
import { selectRegenerateBranch } from '../regenerate-selector.js';
|
|
30
30
|
async function handleRegenerate(args, cwd) {
|
|
31
31
|
if (args.includes('--help') || args.includes('-h')) {
|
|
@@ -198,7 +198,7 @@ async function handleRegenerate(args, cwd) {
|
|
|
198
198
|
if (legacy) {
|
|
199
199
|
if (skipWorkspaceMd)
|
|
200
200
|
console.log(' Note: --no-workspace-md is implicit in legacy mode.');
|
|
201
|
-
const normalizedPath =
|
|
201
|
+
const normalizedPath = projectControlPath(target, 'AIWG.md');
|
|
202
202
|
let existing = '';
|
|
203
203
|
try {
|
|
204
204
|
existing = await fs.readFile(normalizedPath, 'utf8');
|
|
@@ -246,7 +246,7 @@ async function handleRegenerate(args, cwd) {
|
|
|
246
246
|
console.log(` Would regenerate:`);
|
|
247
247
|
if (!skipWorkspaceMd)
|
|
248
248
|
console.log(` - ${path.join(target, 'WORKSPACE.md')} (managed graph; operator section preserved)`);
|
|
249
|
-
console.log(` - ${
|
|
249
|
+
console.log(` - ${projectControlPath(target, 'AIWG.md')}`);
|
|
250
250
|
if (!skipAiwgMd)
|
|
251
251
|
console.log(` - ${aiwgMd}`);
|
|
252
252
|
if (provider === 'claude') {
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { existsSync, readFileSync } from 'fs';
|
|
13
|
-
import { spawnSync } from 'child_process';
|
|
14
13
|
import { createPtyWsHandler, registry as ptyRegistry } from '../../serve/pty-bridge.js';
|
|
15
14
|
import { telemetryStore, createEvent } from '../../serve/telemetry.js';
|
|
16
15
|
import { sandboxRegistry, normalizeSandboxEvent, } from '../../serve/sandbox-registry.js';
|
|
@@ -21,6 +20,7 @@ import { executorRegistry, validateRegisterPayload, validateDispatchPayload, val
|
|
|
21
20
|
import { handleWebhook, IdempotencyCache, PushSecretRegistry, } from '../../a2a/webhook.js';
|
|
22
21
|
import { AiwgError, EXIT_CODES } from '../errors.js';
|
|
23
22
|
import { projectAiwgPath } from '../../config/project-artifacts.js';
|
|
23
|
+
import { loadFeaturePackage } from '../../features/runtime.js';
|
|
24
24
|
// A2A push-notification state — module-scoped so the test harness can
|
|
25
25
|
// monkey-patch them in if needed. One process serves one set of secrets.
|
|
26
26
|
const pushSecretRegistry = new PushSecretRegistry();
|
|
@@ -190,11 +190,10 @@ async function setupWebSockets(httpServer, readOnly) {
|
|
|
190
190
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
191
191
|
let wsMod;
|
|
192
192
|
try {
|
|
193
|
-
|
|
194
|
-
wsMod = await import('ws');
|
|
193
|
+
wsMod = await loadFeaturePackage('ws');
|
|
195
194
|
}
|
|
196
195
|
catch {
|
|
197
|
-
console.warn('[serve]
|
|
196
|
+
console.warn('[serve] WebSocket routes disabled — run `aiwg features install webserver` to enable them.');
|
|
198
197
|
return;
|
|
199
198
|
}
|
|
200
199
|
// ws ships as CJS; ESM import may wrap in .default
|
|
@@ -510,40 +509,19 @@ export async function startServer(opts) {
|
|
|
510
509
|
// import path raises ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING (#1277).
|
|
511
510
|
// hono is an optionalDependency; tsc may not find its types under
|
|
512
511
|
// `npm ci --omit=optional` (e.g. metadata-validation workflow). The
|
|
513
|
-
// try/catch
|
|
512
|
+
// try/catch below emits the managed feature-install route at runtime.
|
|
514
513
|
// @ts-ignore — optional dep; may not be installed at typecheck time
|
|
515
|
-
honoMod = await
|
|
514
|
+
honoMod = await loadFeaturePackage('hono');
|
|
516
515
|
// @ts-ignore — optional dep; may not be installed at typecheck time
|
|
517
|
-
nodeMod = await
|
|
516
|
+
nodeMod = await loadFeaturePackage('@hono/node-server');
|
|
518
517
|
}
|
|
519
518
|
catch {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
message: 'Failed to install serve dependencies (hono, @hono/node-server, ws)',
|
|
527
|
-
hint: 'Install manually: npm install hono @hono/node-server ws',
|
|
528
|
-
exitCode: EXIT_CODES.GENERAL,
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
// Retry imports after install
|
|
532
|
-
try {
|
|
533
|
-
// @ts-ignore — optional dep; may not be installed at typecheck time
|
|
534
|
-
honoMod = await import('hono');
|
|
535
|
-
// @ts-ignore — optional dep; may not be installed at typecheck time
|
|
536
|
-
nodeMod = await import('@hono/node-server');
|
|
537
|
-
}
|
|
538
|
-
catch (err) {
|
|
539
|
-
throw new AiwgError({
|
|
540
|
-
code: 'ERR_SERVE_DEPS_LOAD_FAILED',
|
|
541
|
-
message: 'Serve dependencies installed but could not be loaded',
|
|
542
|
-
hint: 'Try: npm install hono @hono/node-server ws',
|
|
543
|
-
exitCode: EXIT_CODES.GENERAL,
|
|
544
|
-
cause: err,
|
|
545
|
-
});
|
|
546
|
-
}
|
|
519
|
+
throw new AiwgError({
|
|
520
|
+
code: 'ERR_SERVE_DEPS_MISSING',
|
|
521
|
+
message: 'The optional webserver feature is not available',
|
|
522
|
+
hint: 'Run `aiwg features install webserver`, then retry `aiwg serve`.',
|
|
523
|
+
exitCode: EXIT_CODES.GENERAL,
|
|
524
|
+
});
|
|
547
525
|
}
|
|
548
526
|
const { Hono } = honoMod;
|
|
549
527
|
const { serve } = nodeMod;
|
|
@@ -1692,7 +1670,8 @@ export async function startServer(opts) {
|
|
|
1692
1670
|
const webDistDir = path.join(opts.frameworkRoot, 'apps', 'web', 'dist');
|
|
1693
1671
|
if (existsSync(webDistDir)) {
|
|
1694
1672
|
try {
|
|
1695
|
-
|
|
1673
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1674
|
+
const { serveStatic } = await loadFeaturePackage('@hono/node-server/serve-static');
|
|
1696
1675
|
app.use('/*', serveStatic({ root: webDistDir }));
|
|
1697
1676
|
}
|
|
1698
1677
|
catch {
|
|
@@ -1788,7 +1767,7 @@ export const serveHandler = {
|
|
|
1788
1767
|
if (open) {
|
|
1789
1768
|
try {
|
|
1790
1769
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1791
|
-
const openMod = await (
|
|
1770
|
+
const openMod = await loadFeaturePackage('open');
|
|
1792
1771
|
const openBrowser = openMod.default ?? openMod;
|
|
1793
1772
|
await openBrowser(url);
|
|
1794
1773
|
}
|
|
@@ -232,6 +232,7 @@ function installerConsistencyChecks(manifest, manifestDir) {
|
|
|
232
232
|
const recoveryIds = new Set((manifest.spec.recovery ?? []).map((recovery) => recovery.id));
|
|
233
233
|
const osConfigIds = new Set((manifest.spec.os_config ?? []).map((entry) => entry.id));
|
|
234
234
|
const installType = manifest.metadata.install_type ?? 'user';
|
|
235
|
+
const executionMode = manifest.metadata.execution_mode ?? 'deterministic';
|
|
235
236
|
for (const [index, step] of manifest.spec.steps.entries()) {
|
|
236
237
|
if (allStepIds.has(step.id)) {
|
|
237
238
|
findings.push({ severity: 'error', path: `/spec/steps/${index}/id`, rule: 'uniqueStepId', message: `duplicate step id '${step.id}'` });
|
|
@@ -260,7 +261,7 @@ function installerConsistencyChecks(manifest, manifestDir) {
|
|
|
260
261
|
if (!step.instruction) {
|
|
261
262
|
findings.push({ severity: 'error', path: `${pointer}/instruction`, rule: 'agenticInstruction', message: 'agentic step requires instruction' });
|
|
262
263
|
}
|
|
263
|
-
else {
|
|
264
|
+
else if (executionMode !== 'provider-orchestrated') {
|
|
264
265
|
findings.push({ severity: 'warning', path: pointer, rule: 'agenticStep', message: 'agentic steps are exception handling only and require manual intervention during setup-run' });
|
|
265
266
|
}
|
|
266
267
|
}
|
|
@@ -687,6 +688,12 @@ export function runSetupManifest(options) {
|
|
|
687
688
|
return { exitCode: 1, message: 'setup-run: manifest validation failed before execution' };
|
|
688
689
|
}
|
|
689
690
|
const manifest = validation.manifest;
|
|
691
|
+
if (manifest.metadata.execution_mode === 'provider-orchestrated') {
|
|
692
|
+
return {
|
|
693
|
+
exitCode: 2,
|
|
694
|
+
message: 'setup-run: this manifest is provider-orchestrated; give its URL or contents to a supported AI provider instead of executing it as a deterministic CLI manifest',
|
|
695
|
+
};
|
|
696
|
+
}
|
|
690
697
|
const target = detectPlatform(options);
|
|
691
698
|
if (!manifest.spec.platforms.some((candidate) => platformMatches(target, candidate))) {
|
|
692
699
|
return { exitCode: 1, message: `setup-run: platform ${target.os}${target.distro ? `/${target.distro}` : ''}/${target.arch}/${target.shell} is not declared in the manifest` };
|