@aiwg/cli 2026.8.8 → 2026.8.11
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/agents/packaged-agent-inventory.js +37 -1
- 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/fortemi-core-sync.js +23 -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 +63 -17
- 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 +272 -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 +61 -7
- 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
|
@@ -4,7 +4,7 @@ import { createHash } from 'crypto';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import os from 'os';
|
|
6
6
|
import { readAiwgConfig } from '../config/aiwg-config.js';
|
|
7
|
-
import { PROJECT_AIWG_LOCATION_FILE, projectAiwgPath } from '../config/project-artifacts.js';
|
|
7
|
+
import { PROJECT_AIWG_LOCATION_FILE, projectAiwgPath, projectControlPath, } from '../config/project-artifacts.js';
|
|
8
8
|
const DEFAULT_MAX_BYTES = 1_048_576;
|
|
9
9
|
const DEFAULT_REPORT_LIMIT = 20;
|
|
10
10
|
export async function maybeAppendCommandLog(input) {
|
|
@@ -157,7 +157,7 @@ async function findProjectRoot(startDir) {
|
|
|
157
157
|
while (current !== path.dirname(current)) {
|
|
158
158
|
if (existsSync(path.join(current, '.aiwg')) ||
|
|
159
159
|
existsSync(path.join(current, PROJECT_AIWG_LOCATION_FILE)) ||
|
|
160
|
-
existsSync(
|
|
160
|
+
existsSync(projectControlPath(current, 'aiwg.config'))) {
|
|
161
161
|
return current;
|
|
162
162
|
}
|
|
163
163
|
current = path.dirname(current);
|
|
@@ -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 });
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { promises as fs } from 'fs';
|
|
16
16
|
import path from 'path';
|
|
17
17
|
import { createScriptRunner } from './script-runner.js';
|
|
18
|
+
import { createUseHandler } from './use.js';
|
|
18
19
|
import { getFrameworkRoot } from '../../channel/manager.mjs';
|
|
19
20
|
import { refreshAllPackages } from '../../packages/registry.js';
|
|
20
21
|
import { resolveActiveProvider } from '../provider-resolution.js';
|
|
@@ -97,7 +98,14 @@ export async function pruneStaleManagedAgentFiles(options) {
|
|
|
97
98
|
continue;
|
|
98
99
|
const artifactName = normalizeAgentArtifactName(entry.name);
|
|
99
100
|
const missingFromCurrentPackage = !desired.has(artifactName);
|
|
100
|
-
|
|
101
|
+
// Addons have independent manifest versions. Comparing their managed
|
|
102
|
+
// marker to the top-level package version makes a successful refresh
|
|
103
|
+
// delete freshly restored addon agents. Version-based cleanup remains
|
|
104
|
+
// valid for other provider trees that were not refreshed, while the
|
|
105
|
+
// active provider removes only artifacts absent from current sources.
|
|
106
|
+
const fromOlderPackage = provider !== options.provider
|
|
107
|
+
&& currentVersion !== null
|
|
108
|
+
&& isOlderManagedVersion(marker.version, currentVersion);
|
|
101
109
|
if (!missingFromCurrentPackage && !fromOlderPackage)
|
|
102
110
|
continue;
|
|
103
111
|
const relFile = path.relative(options.projectRoot, file);
|
|
@@ -149,7 +157,7 @@ export function collectModelDeployArgs(args) {
|
|
|
149
157
|
export const refreshHandler = {
|
|
150
158
|
id: 'refresh',
|
|
151
159
|
name: 'Refresh',
|
|
152
|
-
description: 'Refresh AIWG to latest version and re-deploy
|
|
160
|
+
description: 'Refresh AIWG to latest version and re-deploy installed frameworks',
|
|
153
161
|
category: 'maintenance',
|
|
154
162
|
aliases: ['--refresh', 'sync', '--sync'],
|
|
155
163
|
async execute(ctx) {
|
|
@@ -163,6 +171,7 @@ export const refreshHandler = {
|
|
|
163
171
|
const modelDeployArgs = collectModelDeployArgs(ctx.args);
|
|
164
172
|
const frameworkRoot = await getFrameworkRoot();
|
|
165
173
|
const runner = createScriptRunner(frameworkRoot);
|
|
174
|
+
const activeUseHandler = createUseHandler();
|
|
166
175
|
if (!quiet) {
|
|
167
176
|
ui.blank();
|
|
168
177
|
// Deprecation notice when invoked as 'sync'
|
|
@@ -195,6 +204,7 @@ export const refreshHandler = {
|
|
|
195
204
|
// Step 2.5: Refresh remote packages (always, unless --packages-only skips npm)
|
|
196
205
|
if (!quiet)
|
|
197
206
|
ui.info(dryRun ? 'Would refresh remote packages...' : 'Refreshing remote packages...');
|
|
207
|
+
const deploymentFailures = [];
|
|
198
208
|
if (!dryRun) {
|
|
199
209
|
try {
|
|
200
210
|
const refreshed = await refreshAllPackages();
|
|
@@ -241,39 +251,67 @@ export const refreshHandler = {
|
|
|
241
251
|
if (!quiet)
|
|
242
252
|
ui.dim(' Skipping package update (--skip-update)');
|
|
243
253
|
}
|
|
244
|
-
// Step 4: Re-deploy frameworks
|
|
245
|
-
|
|
254
|
+
// Step 4: Re-deploy frameworks. Both the default form and --all mean
|
|
255
|
+
// "all installed", never the `aiwg use all` expansion meta-target. This
|
|
256
|
+
// preserves the operator's selected footprint and removal symmetry.
|
|
257
|
+
const refreshConfig = await readAiwgConfig(ctx.cwd);
|
|
258
|
+
const installedFrameworks = Object.keys(refreshConfig?.installed ?? {});
|
|
259
|
+
const requestedFrameworks = frameworksArg
|
|
260
|
+
? frameworksArg.split(',').map(item => item.trim()).filter(Boolean)
|
|
261
|
+
: [];
|
|
262
|
+
const frameworks = !frameworksArg || requestedFrameworks.includes('all')
|
|
263
|
+
? installedFrameworks
|
|
264
|
+
: requestedFrameworks;
|
|
246
265
|
if (!quiet)
|
|
247
266
|
ui.info(dryRun ? 'Would re-deploy frameworks...' : 'Re-deploying frameworks...');
|
|
248
267
|
if (!dryRun) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
268
|
+
if (frameworks.length === 0 && !quiet) {
|
|
269
|
+
ui.dim(' No installed frameworks or addons to re-deploy');
|
|
270
|
+
}
|
|
271
|
+
for (const fw of frameworks) {
|
|
272
|
+
// Invoke the active installation's handler directly. The historical
|
|
273
|
+
// deploy.mjs bridge shells out to the first `aiwg` on PATH, which can
|
|
274
|
+
// be a different version/root and therefore cannot safely refresh
|
|
275
|
+
// addons installed by this package (#143/#2102).
|
|
276
|
+
const useResult = await activeUseHandler.execute({
|
|
277
|
+
...ctx,
|
|
278
|
+
cwd: ctx.cwd,
|
|
279
|
+
frameworkRoot,
|
|
280
|
+
args: [
|
|
281
|
+
fw,
|
|
282
|
+
'--provider', detectedProvider,
|
|
283
|
+
'--target', ctx.cwd,
|
|
284
|
+
'--yes',
|
|
285
|
+
'--json',
|
|
286
|
+
...modelDeployArgs,
|
|
287
|
+
],
|
|
288
|
+
rawArgs: ['use', fw],
|
|
289
|
+
});
|
|
253
290
|
if (useResult.exitCode === 0) {
|
|
254
291
|
if (!quiet)
|
|
255
292
|
ui.success(`Deployed: ${fw}`);
|
|
256
293
|
}
|
|
257
294
|
else {
|
|
295
|
+
deploymentFailures.push(fw);
|
|
258
296
|
if (!quiet)
|
|
259
297
|
ui.warn(`Deploy issue: ${fw} (exit ${useResult.exitCode})`);
|
|
260
298
|
}
|
|
261
299
|
}
|
|
262
300
|
}
|
|
263
301
|
else {
|
|
264
|
-
const targets = frameworks || ['all installed frameworks'];
|
|
265
302
|
if (!quiet) {
|
|
266
|
-
|
|
303
|
+
if (frameworks.length === 0)
|
|
304
|
+
ui.dim(' No installed frameworks or addons');
|
|
305
|
+
for (const fw of frameworks) {
|
|
267
306
|
ui.dim(` Would re-deploy: ${fw}`);
|
|
268
307
|
}
|
|
269
308
|
}
|
|
270
309
|
}
|
|
271
310
|
// Step 4.25: Report planned project-local deploys (#1035).
|
|
272
|
-
// The
|
|
273
|
-
// this block surfaces
|
|
274
|
-
// covered during a real refresh.
|
|
311
|
+
// The active use handler performs the actual project-local deploy during
|
|
312
|
+
// framework refresh; this block surfaces dry-run and completion details.
|
|
275
313
|
try {
|
|
276
|
-
const plDiscovery = await discoverProjectLocalBundles(
|
|
314
|
+
const plDiscovery = await discoverProjectLocalBundles(ctx.cwd);
|
|
277
315
|
const plCount = plDiscovery.bundles.length;
|
|
278
316
|
if (plCount > 0) {
|
|
279
317
|
if (dryRun) {
|
|
@@ -300,11 +338,12 @@ export const refreshHandler = {
|
|
|
300
338
|
if (!quiet)
|
|
301
339
|
ui.info('Checking for stale deployments...');
|
|
302
340
|
let staleAgentRemovals = [];
|
|
303
|
-
if (!dryRun) {
|
|
341
|
+
if (!dryRun && deploymentFailures.length === 0) {
|
|
304
342
|
try {
|
|
305
343
|
staleAgentRemovals = await pruneStaleManagedAgentFiles({
|
|
306
344
|
projectRoot: ctx.cwd,
|
|
307
345
|
frameworkRoot,
|
|
346
|
+
provider: detectedProvider,
|
|
308
347
|
});
|
|
309
348
|
if (staleAgentRemovals.length > 0 && !quiet) {
|
|
310
349
|
const total = staleAgentRemovals.reduce((sum, item) => sum + item.paths.length, 0);
|
|
@@ -419,14 +458,21 @@ export const refreshHandler = {
|
|
|
419
458
|
const output = JSON.stringify({
|
|
420
459
|
status: dryRun ? 'dry-run' : 'refreshed',
|
|
421
460
|
provider: detectedProvider,
|
|
422
|
-
frameworks
|
|
461
|
+
frameworks,
|
|
423
462
|
skipUpdate,
|
|
424
463
|
channel: channel || undefined,
|
|
425
464
|
staleAgentRemovals,
|
|
465
|
+
deploymentFailures,
|
|
426
466
|
});
|
|
427
467
|
console.log(output);
|
|
428
468
|
}
|
|
429
|
-
|
|
469
|
+
if (deploymentFailures.length > 0) {
|
|
470
|
+
return {
|
|
471
|
+
exitCode: 1,
|
|
472
|
+
message: `Failed to re-deploy installed bundle(s): ${deploymentFailures.join(', ')}`,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
return { exitCode: 0 };
|
|
430
476
|
},
|
|
431
477
|
};
|
|
432
478
|
//# sourceMappingURL=refresh.js.map
|
|
@@ -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
|
}
|