@aiwg/cli 2026.7.19 → 2026.7.21
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 +397 -385
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/browser-export.js +7 -0
- package/dist/src/artifacts/citation-parser.js +96 -35
- package/dist/src/artifacts/cli.js +59 -5
- package/dist/src/artifacts/discover-facets.js +15 -0
- package/dist/src/artifacts/discovery-eval.js +290 -0
- package/dist/src/artifacts/fortemi-core-query-adapter.js +1 -1
- package/dist/src/artifacts/fortemi-shard-export.js +1 -1
- package/dist/src/artifacts/index-builder.js +54 -17
- package/dist/src/artifacts/query-engine.js +10 -6
- package/dist/src/artifacts/state-transfer.js +27 -0
- package/dist/src/artifacts/stats.js +8 -0
- package/dist/src/cli/cli-extension-loader.js +73 -0
- package/dist/src/cli/handlers/help.js +2 -1
- package/dist/src/cli/handlers/index.js +6 -2
- package/dist/src/cli/handlers/resource-versions.js +247 -0
- package/dist/src/cli/handlers/sessions.js +966 -0
- package/dist/src/cli/handlers/skill-lint.js +49 -45
- package/dist/src/cli/handlers/subcommands.js +55 -3
- package/dist/src/cli/handlers/use.js +154 -59
- package/dist/src/cli/handlers/utilities.js +49 -34
- package/dist/src/cli/skill-usage.js +146 -24
- package/dist/src/config/cli.js +13 -9
- package/dist/src/config/project-artifacts-runtime.mjs +68 -0
- package/dist/src/config/project-artifacts.js +1 -68
- package/dist/src/extensions/commands/definitions.js +65 -2
- package/dist/src/extensions/manifest.js +29 -0
- package/dist/src/extensions/project-local-discovery.js +86 -2
- package/dist/src/extensions/project-local-remove.js +52 -56
- package/dist/src/extensions/shadow-resolver.js +3 -1
- package/dist/src/plugins/standalone-packager.js +143 -0
- package/dist/src/resources/cache-cleanup.js +67 -0
- package/dist/src/resources/doctor.js +107 -0
- package/dist/src/resources/lockfile.js +125 -0
- package/dist/src/resources/resolver.js +133 -0
- package/dist/src/resources/web-release.d.ts +8 -0
- package/dist/src/resources/web-release.js +159 -1
- package/dist/src/sessions/adapters/claude.js +357 -0
- package/dist/src/sessions/adapters/codex.js +521 -0
- package/dist/src/sessions/adapters/copilot.js +226 -0
- package/dist/src/sessions/adapters/cursor.js +372 -0
- package/dist/src/sessions/adapters/factory.js +345 -0
- package/dist/src/sessions/adapters/generic.js +225 -0
- package/dist/src/sessions/adapters/hermes.js +341 -0
- package/dist/src/sessions/adapters/openclaw.js +381 -0
- package/dist/src/sessions/adapters/opencode.js +454 -0
- package/dist/src/sessions/adapters/openhuman.js +315 -0
- package/dist/src/sessions/adapters/warp.js +160 -0
- package/dist/src/sessions/adapters/windsurf.js +212 -0
- package/dist/src/sessions/candidates.js +210 -0
- package/dist/src/sessions/contracts.js +310 -0
- package/dist/src/sessions/discovery.js +51 -0
- package/dist/src/sessions/fixtures.js +12 -0
- package/dist/src/sessions/importer.js +315 -0
- package/dist/src/sessions/index.js +25 -0
- package/dist/src/sessions/knowledge-shard.js +61 -0
- package/dist/src/sessions/optional-backends.js +238 -0
- package/dist/src/sessions/policy.js +192 -0
- package/dist/src/sessions/ports.js +2 -0
- package/dist/src/sessions/promotion.js +367 -0
- package/dist/src/sessions/readers.js +176 -0
- package/dist/src/sessions/repository.js +1551 -0
- package/dist/src/skills/adapters/agent-skills.js +59 -0
- package/dist/src/skills/adapters/local.js +19 -1
- package/dist/src/skills/agent-skills.js +249 -0
- package/dist/src/skills/cli.js +463 -7
- package/dist/src/skills/deployer.js +554 -0
- package/dist/src/skills/doctor.js +105 -0
- package/dist/src/skills/exporter.js +382 -0
- package/dist/src/skills/importer.js +921 -0
- package/dist/src/skills/registry.js +19 -0
- package/dist/src/skills/validator.js +323 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +5 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +21 -1
- package/dist/src/smiths/context-pipeline/finalization.js +5 -3
- package/dist/src/smiths/context-pipeline/generator.js +4 -1
- package/dist/src/smiths/context-pipeline/parallelism-section.js +34 -1
- package/dist/src/smiths/context-pipeline/workspace-context.js +15 -3
- package/dist/src/smiths/mcpsmith/example.js +3 -1
- package/dist/src/smiths/mcpsmith/generator.js +3 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +2 -1
- package/dist/src/storage/cli.js +3 -2
- package/dist/src/storage/subsystem-cli.js +7 -2
- package/dist/src/update/notifier.mjs +1 -1
- package/dist/src/update/service.mjs +123 -0
- package/package.json +3 -2
|
@@ -20,6 +20,7 @@ import { loadManifest, writeManifest, statMatches, makeEntry } from './checksum-
|
|
|
20
20
|
import { workspaceLinkedFiles } from '../smiths/context-pipeline/workspace-context.js';
|
|
21
21
|
import { normalizeOperationalState } from './operational-state.js';
|
|
22
22
|
import { DEFAULT_PROJECT_AIWG_DIR, resolveProjectAiwgDir, } from '../config/project-artifacts.js';
|
|
23
|
+
import { normalizeStateTransferProjection } from './state-transfer.js';
|
|
23
24
|
function pathContains(parent, child) {
|
|
24
25
|
const relative = path.relative(parent, child);
|
|
25
26
|
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
@@ -322,10 +323,10 @@ export function parseRunbookDoc(data, body, filePath) {
|
|
|
322
323
|
/**
|
|
323
324
|
* Extract trigger phrases from a SKILL.md / agent body.
|
|
324
325
|
*
|
|
325
|
-
* Skills declare alternate activation phrases
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
326
|
+
* Skills declare alternate activation phrases in `triggers`, `aliases`,
|
|
327
|
+
* `deprecated_names`, or under a `## Triggers` heading. This function pulls
|
|
328
|
+
* those names plus each bullet's leading phrase (the part before any `→`
|
|
329
|
+
* arrow or em-dash explanation), lowercased and trimmed.
|
|
329
330
|
*
|
|
330
331
|
* Returns an empty array when no `## Triggers` section is found —
|
|
331
332
|
* non-skill artifacts get `triggers: undefined` after this is wired.
|
|
@@ -334,18 +335,20 @@ export function parseRunbookDoc(data, body, filePath) {
|
|
|
334
335
|
*/
|
|
335
336
|
export function extractTriggers(body, frontmatter) {
|
|
336
337
|
const phrases = [];
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
338
|
+
for (const field of ['triggers', 'aliases', 'deprecated_names']) {
|
|
339
|
+
const declaredValues = Array.isArray(frontmatter?.[field])
|
|
340
|
+
? frontmatter[field]
|
|
341
|
+
: [];
|
|
342
|
+
for (const value of declaredValues) {
|
|
343
|
+
if (typeof value !== 'string')
|
|
344
|
+
continue;
|
|
345
|
+
const phrase = value.trim().toLowerCase();
|
|
346
|
+
if (phrase.length === 0)
|
|
347
|
+
continue;
|
|
348
|
+
if (phrase.length > 200)
|
|
349
|
+
continue;
|
|
350
|
+
phrases.push(phrase);
|
|
351
|
+
}
|
|
349
352
|
}
|
|
350
353
|
// Find a triggers heading (case-insensitive). Accepted variants:
|
|
351
354
|
// ## Triggers
|
|
@@ -882,6 +885,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
882
885
|
// Script entrypoint metadata is meaningful for skills only (#1227).
|
|
883
886
|
const script = type === 'skill' ? extractSkillScript(data) : undefined;
|
|
884
887
|
const operationalState = normalizeOperationalState(data.operational_state);
|
|
888
|
+
const stateTransfer = normalizeStateTransferProjection(data.state_transfer);
|
|
885
889
|
// Canonical short name (#1233) — used by the scorer to floor exact-name
|
|
886
890
|
// queries to 1.0 so hyphenated kernel-skill names like `aiwg-doctor`
|
|
887
891
|
// remain searchable even when the rendered title strips the hyphen.
|
|
@@ -907,6 +911,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
907
911
|
...(kernel ? { kernel } : {}),
|
|
908
912
|
...(script ? { script } : {}),
|
|
909
913
|
...(operationalState ? { operationalState } : {}),
|
|
914
|
+
...(stateTransfer ? { stateTransfer } : {}),
|
|
910
915
|
};
|
|
911
916
|
}
|
|
912
917
|
entries[relativePath] = entry;
|
|
@@ -957,6 +962,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
957
962
|
}
|
|
958
963
|
}
|
|
959
964
|
// Run citation sidecar edge extraction if configured
|
|
965
|
+
let citationMetrics = null;
|
|
960
966
|
if (graphConfig?.edgeExtraction?.parser === 'citation-sidecar') {
|
|
961
967
|
// Build REF-XXX → path map from all entries with ref frontmatter
|
|
962
968
|
const entryFrontmatter = new Map();
|
|
@@ -971,6 +977,10 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
971
977
|
const refToPath = buildRefToPathMap(entryFrontmatter);
|
|
972
978
|
// Parse each entry as a citation sidecar and extract edges
|
|
973
979
|
let citationEdgeCount = 0;
|
|
980
|
+
let outgoingDeclarations = 0;
|
|
981
|
+
let incomingDeclarations = 0;
|
|
982
|
+
const canonicalOutgoing = new Set();
|
|
983
|
+
const declaredIncoming = new Set();
|
|
974
984
|
for (const entryPath of Object.keys(entries)) {
|
|
975
985
|
const fullPath = absoluteEntryPath(cwd, entryPath, graph);
|
|
976
986
|
if (!fs.existsSync(fullPath))
|
|
@@ -979,6 +989,14 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
979
989
|
const result = parseCitationSidecar(content);
|
|
980
990
|
if (!result)
|
|
981
991
|
continue;
|
|
992
|
+
outgoingDeclarations += result.cites.length;
|
|
993
|
+
incomingDeclarations += result.citedBy.length;
|
|
994
|
+
for (const targetRef of result.cites) {
|
|
995
|
+
canonicalOutgoing.add(`${result.ref}\0${targetRef}`);
|
|
996
|
+
}
|
|
997
|
+
for (const sourceRef of result.citedBy) {
|
|
998
|
+
declaredIncoming.add(`${sourceRef}\0${result.ref}`);
|
|
999
|
+
}
|
|
982
1000
|
const edges = citationResultToEdges(result, refToPath);
|
|
983
1001
|
if (!depGraph[entryPath]) {
|
|
984
1002
|
depGraph[entryPath] = { upstream: [], downstream: [] };
|
|
@@ -1004,6 +1022,15 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1004
1022
|
citationEdgeCount++;
|
|
1005
1023
|
}
|
|
1006
1024
|
}
|
|
1025
|
+
citationMetrics = {
|
|
1026
|
+
canonicalEdges: canonicalOutgoing.size,
|
|
1027
|
+
outgoingDeclarations,
|
|
1028
|
+
incomingDeclarations,
|
|
1029
|
+
unmirroredOutgoing: [...canonicalOutgoing]
|
|
1030
|
+
.filter(edge => !declaredIncoming.has(edge)).length,
|
|
1031
|
+
unmirroredIncoming: [...declaredIncoming]
|
|
1032
|
+
.filter(edge => !canonicalOutgoing.has(edge)).length,
|
|
1033
|
+
};
|
|
1007
1034
|
if (verbose && citationEdgeCount > 0) {
|
|
1008
1035
|
console.log(` citation edges: ${citationEdgeCount}`);
|
|
1009
1036
|
}
|
|
@@ -1038,7 +1065,9 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1038
1065
|
manifestStats.pruned = 0;
|
|
1039
1066
|
writeManifest(indexOutputDir, nextManifest);
|
|
1040
1067
|
// Write stats
|
|
1041
|
-
const
|
|
1068
|
+
const downstreamEdges = Object.values(depGraph).reduce((sum, node) => sum + node.downstream.length, 0);
|
|
1069
|
+
const adjacencyEntries = Object.values(depGraph).reduce((sum, node) => sum + node.upstream.length + node.downstream.length, 0);
|
|
1070
|
+
const totalEdges = citationMetrics?.canonicalEdges ?? downstreamEdges;
|
|
1042
1071
|
const orphaned = Object.entries(depGraph).filter(([, node]) => node.upstream.length === 0 && node.downstream.length === 0).length;
|
|
1043
1072
|
const mostReferenced = Object.entries(depGraph)
|
|
1044
1073
|
.map(([p, node]) => ({ path: p, count: node.downstream.length }))
|
|
@@ -1064,6 +1093,14 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1064
1093
|
tagDistribution: tagDist,
|
|
1065
1094
|
graphMetrics: {
|
|
1066
1095
|
totalEdges,
|
|
1096
|
+
...(citationMetrics ? {
|
|
1097
|
+
canonicalEdges: citationMetrics.canonicalEdges,
|
|
1098
|
+
outgoingDeclarations: citationMetrics.outgoingDeclarations,
|
|
1099
|
+
incomingDeclarations: citationMetrics.incomingDeclarations,
|
|
1100
|
+
adjacencyEntries,
|
|
1101
|
+
unmirroredOutgoing: citationMetrics.unmirroredOutgoing,
|
|
1102
|
+
unmirroredIncoming: citationMetrics.unmirroredIncoming,
|
|
1103
|
+
} : {}),
|
|
1067
1104
|
orphanedArtifacts: orphaned,
|
|
1068
1105
|
mostReferenced,
|
|
1069
1106
|
},
|
|
@@ -1740,14 +1740,18 @@ export async function showArtifact(cwd, params) {
|
|
|
1740
1740
|
try {
|
|
1741
1741
|
if (!webRelease)
|
|
1742
1742
|
throw new Error('verified web release context is unavailable');
|
|
1743
|
-
const {
|
|
1744
|
-
const
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1743
|
+
const { logicalIdFromFirstPartyPath, resolveAiwgResourceBytes } = await import('../resources/resolver.js');
|
|
1744
|
+
const logicalId = logicalIdFromFirstPartyPath(entry.path);
|
|
1745
|
+
if (!logicalId)
|
|
1746
|
+
throw new Error(`indexed path is not a first-party AIWG resource: ${entry.path}`);
|
|
1747
|
+
const resolved = await resolveAiwgResourceBytes(logicalId, {
|
|
1748
|
+
source: 'web',
|
|
1749
|
+
frameworkRoot: aiwgRoot ?? cwd,
|
|
1750
|
+
webRelease,
|
|
1751
|
+
webReleaseOptions: params.webReleaseOptions,
|
|
1748
1752
|
offline: params.offline,
|
|
1749
1753
|
});
|
|
1750
|
-
content = bytes.toString('utf8');
|
|
1754
|
+
content = resolved.bytes.toString('utf8');
|
|
1751
1755
|
}
|
|
1752
1756
|
catch (error) {
|
|
1753
1757
|
console.error(`Error: AIWG web resource show failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function normalizeStateTransferProjection(value) {
|
|
2
|
+
if (value === undefined)
|
|
3
|
+
return undefined;
|
|
4
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
5
|
+
throw new Error("state_transfer must be an object");
|
|
6
|
+
}
|
|
7
|
+
const record = value;
|
|
8
|
+
const unknown = Object.keys(record).filter((key) => key !== "deleted_at");
|
|
9
|
+
if (unknown.length > 0) {
|
|
10
|
+
throw new Error(`state_transfer has unsupported fields: ${unknown.join(", ")}`);
|
|
11
|
+
}
|
|
12
|
+
if (!Object.hasOwn(record, "deleted_at")) {
|
|
13
|
+
throw new Error("state_transfer.deleted_at is required");
|
|
14
|
+
}
|
|
15
|
+
const deletedAt = record.deleted_at;
|
|
16
|
+
if (deletedAt === null)
|
|
17
|
+
return { deletedAt: null };
|
|
18
|
+
if (deletedAt instanceof Date && !Number.isNaN(deletedAt.getTime())) {
|
|
19
|
+
return { deletedAt: deletedAt.toISOString() };
|
|
20
|
+
}
|
|
21
|
+
if (typeof deletedAt !== "string"
|
|
22
|
+
|| Number.isNaN(Date.parse(deletedAt))) {
|
|
23
|
+
throw new Error("state_transfer.deleted_at must be null or an ISO date-time");
|
|
24
|
+
}
|
|
25
|
+
return { deletedAt: new Date(deletedAt).toISOString() };
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=state-transfer.js.map
|
|
@@ -153,6 +153,14 @@ async function renderStats(cwd, stats, options, graphType) {
|
|
|
153
153
|
// Dependency graph
|
|
154
154
|
console.log('Dependency Graph:');
|
|
155
155
|
console.log(` Total edges: ${stats.graphMetrics.totalEdges}`);
|
|
156
|
+
if (stats.graphMetrics.canonicalEdges !== undefined) {
|
|
157
|
+
console.log(` Canonical edges: ${stats.graphMetrics.canonicalEdges}`);
|
|
158
|
+
console.log(` Outgoing declares: ${stats.graphMetrics.outgoingDeclarations}`);
|
|
159
|
+
console.log(` Incoming declares: ${stats.graphMetrics.incomingDeclarations}`);
|
|
160
|
+
console.log(` Adjacency entries: ${stats.graphMetrics.adjacencyEntries}`);
|
|
161
|
+
console.log(` Unmirrored outgoing:${String(stats.graphMetrics.unmirroredOutgoing).padStart(3)}`);
|
|
162
|
+
console.log(` Unmirrored incoming:${String(stats.graphMetrics.unmirroredIncoming).padStart(3)}`);
|
|
163
|
+
}
|
|
156
164
|
console.log(` Orphaned artifacts: ${stats.graphMetrics.orphanedArtifacts}`);
|
|
157
165
|
if (stats.graphMetrics.mostReferenced) {
|
|
158
166
|
console.log(` Most referenced: ${stats.graphMetrics.mostReferenced.path} (${stats.graphMetrics.mostReferenced.count} dependents)`);
|
|
@@ -41,10 +41,83 @@ export async function writeRegistry(cwd, registry) {
|
|
|
41
41
|
* Called by `aiwg use <addon>` after reading the addon manifest.
|
|
42
42
|
*/
|
|
43
43
|
export async function registerCliCommands(cwd, namespace, description, source, subcommands) {
|
|
44
|
+
if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(namespace)) {
|
|
45
|
+
throw new Error(`Invalid CLI namespace: ${namespace}`);
|
|
46
|
+
}
|
|
47
|
+
if (!source || !path.isAbsolute(source)) {
|
|
48
|
+
throw new Error('CLI extension source must be an absolute path.');
|
|
49
|
+
}
|
|
50
|
+
if (Object.keys(subcommands).length === 0) {
|
|
51
|
+
throw new Error(`CLI namespace '${namespace}' declares no subcommands.`);
|
|
52
|
+
}
|
|
53
|
+
for (const [name, subcommand] of Object.entries(subcommands)) {
|
|
54
|
+
if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(name)) {
|
|
55
|
+
throw new Error(`Invalid CLI subcommand: ${namespace} ${name}`);
|
|
56
|
+
}
|
|
57
|
+
if (!subcommand
|
|
58
|
+
|| typeof subcommand.file !== 'string'
|
|
59
|
+
|| !/^[a-zA-Z0-9_-]+\.mjs$/.test(subcommand.file)) {
|
|
60
|
+
throw new Error(`CLI subcommand '${namespace} ${name}' must name a local .mjs file.`);
|
|
61
|
+
}
|
|
62
|
+
if (typeof subcommand.description !== 'string' || !subcommand.description.trim()) {
|
|
63
|
+
throw new Error(`CLI subcommand '${namespace} ${name}' requires a description.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
44
66
|
const existing = await readRegistry(cwd) ?? {};
|
|
45
67
|
existing[namespace] = { source, description, subcommands };
|
|
46
68
|
await writeRegistry(cwd, existing);
|
|
47
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Read and validate an addon-shaped manifest's expandable CLI declaration.
|
|
72
|
+
*
|
|
73
|
+
* `sourceRoot` may be a bundled addon, a project-local addon, or the validated
|
|
74
|
+
* payload of a project-local plugin wrapper. Module files are constrained to
|
|
75
|
+
* the declared commands directory; registration never follows `..` paths.
|
|
76
|
+
*/
|
|
77
|
+
export async function loadCliCommandsContribution(sourceRoot) {
|
|
78
|
+
const manifestPath = path.join(sourceRoot, 'manifest.json');
|
|
79
|
+
let raw;
|
|
80
|
+
try {
|
|
81
|
+
raw = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error.code === 'ENOENT')
|
|
85
|
+
return null;
|
|
86
|
+
throw new Error(`Unable to read CLI extension manifest at ${manifestPath}: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
const candidate = raw.cli_commands;
|
|
89
|
+
if (candidate === undefined)
|
|
90
|
+
return null;
|
|
91
|
+
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
92
|
+
throw new Error(`Invalid cli_commands block in ${manifestPath}`);
|
|
93
|
+
}
|
|
94
|
+
const commands = candidate;
|
|
95
|
+
if (typeof commands.namespace !== 'string'
|
|
96
|
+
|| typeof commands.description !== 'string'
|
|
97
|
+
|| !commands.subcommands
|
|
98
|
+
|| typeof commands.subcommands !== 'object'
|
|
99
|
+
|| Array.isArray(commands.subcommands)) {
|
|
100
|
+
throw new Error(`Incomplete cli_commands block in ${manifestPath}`);
|
|
101
|
+
}
|
|
102
|
+
const entry = commands.entry ?? 'commands/';
|
|
103
|
+
if (typeof entry !== 'string'
|
|
104
|
+
|| path.isAbsolute(entry)
|
|
105
|
+
|| entry.split(/[\\/]+/).includes('..')) {
|
|
106
|
+
throw new Error(`Unsafe cli_commands.entry in ${manifestPath}`);
|
|
107
|
+
}
|
|
108
|
+
const commandsSource = path.resolve(sourceRoot, entry);
|
|
109
|
+
const relative = path.relative(path.resolve(sourceRoot), commandsSource);
|
|
110
|
+
if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
111
|
+
throw new Error(`cli_commands.entry escapes its addon root in ${manifestPath}`);
|
|
112
|
+
}
|
|
113
|
+
const manifest = {
|
|
114
|
+
namespace: commands.namespace,
|
|
115
|
+
description: commands.description,
|
|
116
|
+
entry,
|
|
117
|
+
subcommands: commands.subcommands,
|
|
118
|
+
};
|
|
119
|
+
return { manifest, commandsSource };
|
|
120
|
+
}
|
|
48
121
|
/**
|
|
49
122
|
* Try to execute an addon-contributed CLI command
|
|
50
123
|
*
|
|
@@ -80,6 +80,7 @@ function displayHelp() {
|
|
|
80
80
|
helpGroup('DISCOVERY', [
|
|
81
81
|
['discover "<phrase>"', 'Find skills/agents/commands/rules by capability'],
|
|
82
82
|
['show <type> <name>', 'Stream the body of an indexed artifact'],
|
|
83
|
+
['versions <list|resolve|show>', 'Browse and resolve signed AIWG web resource releases'],
|
|
83
84
|
['index <subcommand>', 'Manage the artifact index (build/query/discover/deps/stats)'],
|
|
84
85
|
['artifacts move --to <path>', 'Move/rename the project AIWG artifact root and reindex'],
|
|
85
86
|
]);
|
|
@@ -120,7 +121,7 @@ function displayHelp() {
|
|
|
120
121
|
['doctor', 'Check installation health'],
|
|
121
122
|
['version', 'Show version and channel info'],
|
|
122
123
|
['refresh', 'Update AIWG and redeploy frameworks (formerly: sync)'],
|
|
123
|
-
['update', '
|
|
124
|
+
['update', 'Update the active installation and re-deploy installed frameworks (alias: upgrade)'],
|
|
124
125
|
['help', 'Show this help message'],
|
|
125
126
|
]);
|
|
126
127
|
helpGroup('CHANNEL', [
|
|
@@ -44,6 +44,7 @@ import { serveHandler } from './serve.js';
|
|
|
44
44
|
import { lintHandler } from './lint.js';
|
|
45
45
|
import { feedbackHandler } from './feedback.js';
|
|
46
46
|
import { sessionHandler } from './session.js';
|
|
47
|
+
import { sessionsHandler } from './sessions.js';
|
|
47
48
|
import { sandboxHandler, sandboxHandlers } from './sandbox.js';
|
|
48
49
|
import { diagnoseHandler } from './diagnose.js';
|
|
49
50
|
import { localExecutorHandler, localExecutorServeHandler } from './local-executor.js';
|
|
@@ -54,6 +55,7 @@ import { cockpitHandler } from './cockpit.js';
|
|
|
54
55
|
import { commandLogHandler } from './command-log.js';
|
|
55
56
|
import { skillUsageHandler } from './skill-usage.js';
|
|
56
57
|
import { modelsHandler } from './models.js';
|
|
58
|
+
import { versionsHandler } from './resource-versions.js';
|
|
57
59
|
// Re-export individual handlers
|
|
58
60
|
export {
|
|
59
61
|
// Maintenance
|
|
@@ -61,11 +63,11 @@ helpHandler, versionHandler, doctorHandler, updateHandler, refreshHandler, regen
|
|
|
61
63
|
// Framework management
|
|
62
64
|
useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, issueHandler, issueAuditHandler, runHandler,
|
|
63
65
|
// Project
|
|
64
|
-
newBundleHandler, quickrefHandler, newProjectHandler,
|
|
66
|
+
newBundleHandler, quickrefHandler, newProjectHandler, sessionHandler, sessionsHandler,
|
|
65
67
|
// Workspace
|
|
66
68
|
statusHandler, wizardHandler, migrateWorkspaceHandler, rollbackWorkspaceHandler,
|
|
67
69
|
// Subcommands
|
|
68
|
-
mcpHandler, catalogHandler, modelsHandler, indexHandler, artifactsHandler, corpusHandler, discoverHandler, showHandler, featuresHandler, skillsHandler, configHandler, opsHandler, storageHandler, activityLogHandler, commandLogHandler, skillUsageHandler, kbHandler, memoryHandler, reflectionsHandler, provenanceHandler, researchStoreHandler, researchQueryHandler, runtimeInfoHandler, agentcardHandler,
|
|
70
|
+
mcpHandler, catalogHandler, modelsHandler, versionsHandler, indexHandler, artifactsHandler, corpusHandler, discoverHandler, showHandler, featuresHandler, skillsHandler, configHandler, opsHandler, storageHandler, activityLogHandler, commandLogHandler, skillUsageHandler, kbHandler, memoryHandler, reflectionsHandler, provenanceHandler, researchStoreHandler, researchQueryHandler, runtimeInfoHandler, agentcardHandler,
|
|
69
71
|
// Agentic Tools (RLM)
|
|
70
72
|
chunkHandler, fanoutHandler, rlmPrepHandler, rlmSearchHandler, rlmStatusCliHandler, rlmCacheHandler,
|
|
71
73
|
// Utilities
|
|
@@ -139,6 +141,7 @@ export const allHandlers = [
|
|
|
139
141
|
mcpHandler,
|
|
140
142
|
catalogHandler,
|
|
141
143
|
modelsHandler,
|
|
144
|
+
versionsHandler,
|
|
142
145
|
indexHandler,
|
|
143
146
|
artifactsHandler,
|
|
144
147
|
corpusHandler,
|
|
@@ -222,6 +225,7 @@ export const allHandlers = [
|
|
|
222
225
|
feedbackHandler,
|
|
223
226
|
// Session (#884)
|
|
224
227
|
sessionHandler,
|
|
228
|
+
sessionsHandler,
|
|
225
229
|
// Repo access policy (#1376)
|
|
226
230
|
...repoAccessHandlers,
|
|
227
231
|
];
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { loadResourceTrustRootFile, readVerifiedRegularFile, resolveWebRelease, } from "../../resources/web-release.js";
|
|
3
|
+
import { getProjectDir } from "../../config/aiwg-config.js";
|
|
4
|
+
import { cleanWebResourceCache } from "../../resources/cache-cleanup.js";
|
|
5
|
+
import { writeWebResourceLock } from "../../resources/lockfile.js";
|
|
6
|
+
const MAX_RESOURCE_MANIFEST_BYTES = 4 * 1024 * 1024;
|
|
7
|
+
const DEFAULT_CHANNELS = ["stable", "latest", "canary", "main"];
|
|
8
|
+
function usage() {
|
|
9
|
+
return [
|
|
10
|
+
"Usage: aiwg versions <list|resolve|show|clean-cache> [selector] [--json] [--pretty] [--offline]",
|
|
11
|
+
"",
|
|
12
|
+
"Examples:",
|
|
13
|
+
" aiwg versions list --json",
|
|
14
|
+
" aiwg versions resolve stable --json",
|
|
15
|
+
" aiwg versions resolve stable --write-lock",
|
|
16
|
+
" aiwg versions show 2026.7.18",
|
|
17
|
+
" aiwg versions clean-cache --dry-run",
|
|
18
|
+
].join("\n");
|
|
19
|
+
}
|
|
20
|
+
function flagValue(args, flag) {
|
|
21
|
+
const index = args.indexOf(flag);
|
|
22
|
+
if (index === -1)
|
|
23
|
+
return undefined;
|
|
24
|
+
const value = args[index + 1];
|
|
25
|
+
if (!value || value.startsWith("--"))
|
|
26
|
+
throw new Error(`${flag} requires a value`);
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
function parseArgs(args) {
|
|
30
|
+
const [rawSubcommand = "list", ...rest] = args;
|
|
31
|
+
if (rawSubcommand !== "list" &&
|
|
32
|
+
rawSubcommand !== "resolve" &&
|
|
33
|
+
rawSubcommand !== "show" &&
|
|
34
|
+
rawSubcommand !== "clean-cache") {
|
|
35
|
+
throw new Error(`Unknown versions subcommand: ${rawSubcommand}\n\n${usage()}`);
|
|
36
|
+
}
|
|
37
|
+
const valueFlagIndexes = new Set();
|
|
38
|
+
for (const flag of ["--channels", "--target", "--prefix"]) {
|
|
39
|
+
const index = rest.indexOf(flag);
|
|
40
|
+
if (index !== -1) {
|
|
41
|
+
valueFlagIndexes.add(index);
|
|
42
|
+
valueFlagIndexes.add(index + 1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const positionals = rest.filter((arg, index) => !arg.startsWith("--") && !valueFlagIndexes.has(index));
|
|
46
|
+
if ((rawSubcommand === "resolve" || rawSubcommand === "show") && positionals.length !== 1) {
|
|
47
|
+
throw new Error(`aiwg versions ${rawSubcommand} requires exactly one version, range, digest, or channel selector\n\n${usage()}`);
|
|
48
|
+
}
|
|
49
|
+
if (rawSubcommand === "list" && positionals.length > 0) {
|
|
50
|
+
throw new Error(`aiwg versions list does not accept positional selectors\n\n${usage()}`);
|
|
51
|
+
}
|
|
52
|
+
if (rawSubcommand === "clean-cache" && positionals.length > 0) {
|
|
53
|
+
throw new Error(`aiwg versions clean-cache does not accept positional selectors\n\n${usage()}`);
|
|
54
|
+
}
|
|
55
|
+
const writeLock = rest.includes("--write-lock");
|
|
56
|
+
if ((rawSubcommand === "list" || rawSubcommand === "clean-cache") && writeLock) {
|
|
57
|
+
throw new Error(`aiwg versions ${rawSubcommand} cannot write resources.lock.json; use resolve or show with --write-lock`);
|
|
58
|
+
}
|
|
59
|
+
const channelsValue = flagValue(rest, "--channels");
|
|
60
|
+
const channels = channelsValue
|
|
61
|
+
? channelsValue.split(",").map((channel) => channel.trim()).filter(Boolean)
|
|
62
|
+
: [...DEFAULT_CHANNELS];
|
|
63
|
+
if (channels.length === 0)
|
|
64
|
+
throw new Error("--channels must include at least one channel");
|
|
65
|
+
return {
|
|
66
|
+
subcommand: rawSubcommand,
|
|
67
|
+
selector: positionals[0],
|
|
68
|
+
json: rest.includes("--json") || rest.includes("--format=json"),
|
|
69
|
+
pretty: rest.includes("--pretty"),
|
|
70
|
+
offline: rest.includes("--offline"),
|
|
71
|
+
writeLock,
|
|
72
|
+
dryRun: rest.includes("--dry-run"),
|
|
73
|
+
force: rest.includes("--force"),
|
|
74
|
+
channels,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function webReleaseOptionsFromEnvironment() {
|
|
78
|
+
const baseUrl = process.env.AIWG_RESOURCE_BASE_URL;
|
|
79
|
+
const cacheRoot = process.env.AIWG_RESOURCE_CACHE_ROOT;
|
|
80
|
+
const trustRootFile = process.env.AIWG_RESOURCE_TRUST_ROOT_FILE;
|
|
81
|
+
const publicKeyPem = trustRootFile === undefined
|
|
82
|
+
? undefined
|
|
83
|
+
: loadResourceTrustRootFile(path.resolve(trustRootFile));
|
|
84
|
+
return {
|
|
85
|
+
...(baseUrl === undefined ? {} : { baseUrl }),
|
|
86
|
+
...(cacheRoot === undefined ? {} : { cacheRoot }),
|
|
87
|
+
...(publicKeyPem === undefined ? {} : { publicKeyPem }),
|
|
88
|
+
...(process.env.AIWG_RESOURCE_ALLOW_INSECURE_LOOPBACK_HTTP === "1"
|
|
89
|
+
? { allowInsecureLoopbackHttp: true }
|
|
90
|
+
: {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function readManifestSummary(release) {
|
|
94
|
+
const bytes = readVerifiedRegularFile(release.releaseManifestPath, {
|
|
95
|
+
label: "verified AIWG resource release manifest",
|
|
96
|
+
maxBytes: MAX_RESOURCE_MANIFEST_BYTES,
|
|
97
|
+
expectedSha256: release.manifestDigest,
|
|
98
|
+
});
|
|
99
|
+
const value = JSON.parse(bytes.toString("utf8"));
|
|
100
|
+
const bundles = Array.isArray(value.bundles) ? value.bundles : [];
|
|
101
|
+
const files = Array.isArray(value.files) ? value.files : [];
|
|
102
|
+
return {
|
|
103
|
+
schemaVersion: value.schemaVersion,
|
|
104
|
+
version: value.version,
|
|
105
|
+
compatibility: value.compatibility,
|
|
106
|
+
source: value.source,
|
|
107
|
+
bundles,
|
|
108
|
+
fileCount: files.length,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function releaseJson(release, manifest, lockfilePath) {
|
|
112
|
+
return {
|
|
113
|
+
selector: release.selector,
|
|
114
|
+
selectorKind: release.selectorKind,
|
|
115
|
+
version: release.version,
|
|
116
|
+
manifestSha256: release.manifestDigest,
|
|
117
|
+
manifestUrl: release.manifestUrl,
|
|
118
|
+
baseUrl: release.baseUrl,
|
|
119
|
+
cacheDir: release.cacheDir,
|
|
120
|
+
channelSequence: release.channelSequence,
|
|
121
|
+
fortemiCore: {
|
|
122
|
+
manifestSha256: release.fortemiManifestSha256,
|
|
123
|
+
manifestSize: release.fortemiManifestSize,
|
|
124
|
+
exportSha256: release.fortemiExportSha256,
|
|
125
|
+
exportSize: release.fortemiExportSize,
|
|
126
|
+
},
|
|
127
|
+
descriptorCount: release.descriptors.size,
|
|
128
|
+
...(lockfilePath === undefined ? {} : { lockfile: lockfilePath }),
|
|
129
|
+
...(manifest === undefined ? {} : { manifest }),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function printJson(value, pretty) {
|
|
133
|
+
console.log(JSON.stringify(value, null, pretty ? 2 : 0));
|
|
134
|
+
}
|
|
135
|
+
function printReleaseText(release, manifest, lockfilePath) {
|
|
136
|
+
console.log(`selector: ${release.selector} (${release.selectorKind})`);
|
|
137
|
+
console.log(`version: ${release.version}`);
|
|
138
|
+
if (release.channelSequence !== undefined)
|
|
139
|
+
console.log(`channel_sequence: ${release.channelSequence}`);
|
|
140
|
+
console.log(`manifest_sha256: ${release.manifestDigest}`);
|
|
141
|
+
console.log(`manifest_url: ${release.manifestUrl}`);
|
|
142
|
+
console.log(`cache_dir: ${release.cacheDir}`);
|
|
143
|
+
console.log(`fortemi_manifest_sha256: ${release.fortemiManifestSha256}`);
|
|
144
|
+
console.log(`fortemi_export_sha256: ${release.fortemiExportSha256}`);
|
|
145
|
+
console.log(`descriptor_count: ${release.descriptors.size}`);
|
|
146
|
+
if (lockfilePath !== undefined)
|
|
147
|
+
console.log(`lockfile: ${lockfilePath}`);
|
|
148
|
+
if (manifest) {
|
|
149
|
+
console.log(`schema_version: ${String(manifest.schemaVersion)}`);
|
|
150
|
+
console.log(`file_count: ${manifest.fileCount}`);
|
|
151
|
+
console.log(`bundle_count: ${manifest.bundles.length}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export const versionsHandler = {
|
|
155
|
+
id: "versions",
|
|
156
|
+
name: "Resource Versions",
|
|
157
|
+
description: "Browse and resolve signed AIWG web resource releases",
|
|
158
|
+
category: "index",
|
|
159
|
+
aliases: [],
|
|
160
|
+
async execute(ctx) {
|
|
161
|
+
if (ctx.args[0] === "help" || ctx.args[0] === "--help" || ctx.args[0] === "-h") {
|
|
162
|
+
console.log(usage());
|
|
163
|
+
return { exitCode: 0 };
|
|
164
|
+
}
|
|
165
|
+
let parsed;
|
|
166
|
+
try {
|
|
167
|
+
parsed = parseArgs(ctx.args);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return { exitCode: 2, message: error instanceof Error ? error.message : String(error) };
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const baseOptions = webReleaseOptionsFromEnvironment();
|
|
174
|
+
if (parsed.subcommand === "clean-cache") {
|
|
175
|
+
const result = cleanWebResourceCache(getProjectDir(ctx, ctx.args), {
|
|
176
|
+
cacheRoot: process.env.AIWG_RESOURCE_CACHE_ROOT,
|
|
177
|
+
dryRun: parsed.dryRun,
|
|
178
|
+
force: parsed.force,
|
|
179
|
+
});
|
|
180
|
+
if (parsed.json) {
|
|
181
|
+
printJson(result, parsed.pretty);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.log(`cache_root: ${result.cacheRoot}`);
|
|
185
|
+
console.log(`dry_run: ${result.dryRun}`);
|
|
186
|
+
console.log(`force: ${result.force}`);
|
|
187
|
+
console.log(`locked: ${result.locked.length}`);
|
|
188
|
+
console.log(`preserved: ${result.preserved.length}`);
|
|
189
|
+
console.log(`removed: ${result.removed.length}`);
|
|
190
|
+
console.log(`skipped: ${result.skipped.length}`);
|
|
191
|
+
}
|
|
192
|
+
return { exitCode: 0 };
|
|
193
|
+
}
|
|
194
|
+
if (parsed.subcommand === "list") {
|
|
195
|
+
const resolved = [];
|
|
196
|
+
const unavailable = [];
|
|
197
|
+
for (const channel of parsed.channels) {
|
|
198
|
+
try {
|
|
199
|
+
const release = await resolveWebRelease({
|
|
200
|
+
...baseOptions,
|
|
201
|
+
selector: channel,
|
|
202
|
+
offline: parsed.offline,
|
|
203
|
+
});
|
|
204
|
+
resolved.push(releaseJson(release));
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
unavailable.push({
|
|
208
|
+
channel,
|
|
209
|
+
error: error instanceof Error ? error.message : String(error),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (parsed.json) {
|
|
214
|
+
printJson({ channels: resolved, unavailable }, parsed.pretty);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
for (const release of resolved) {
|
|
218
|
+
console.log(`${release.selector}: ${release.version} ${release.manifestSha256}`);
|
|
219
|
+
}
|
|
220
|
+
if (resolved.length === 0)
|
|
221
|
+
console.log("No configured channels resolved.");
|
|
222
|
+
}
|
|
223
|
+
return { exitCode: 0 };
|
|
224
|
+
}
|
|
225
|
+
const release = await resolveWebRelease({
|
|
226
|
+
...baseOptions,
|
|
227
|
+
selector: parsed.selector,
|
|
228
|
+
offline: parsed.offline,
|
|
229
|
+
});
|
|
230
|
+
const manifest = parsed.subcommand === "show" ? readManifestSummary(release) : undefined;
|
|
231
|
+
const lockfilePath = parsed.writeLock
|
|
232
|
+
? writeWebResourceLock(getProjectDir(ctx, ctx.args), release).path
|
|
233
|
+
: undefined;
|
|
234
|
+
if (parsed.json) {
|
|
235
|
+
printJson(releaseJson(release, manifest, lockfilePath), parsed.pretty);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
printReleaseText(release, manifest, lockfilePath);
|
|
239
|
+
}
|
|
240
|
+
return { exitCode: 0 };
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
return { exitCode: 1, message: error instanceof Error ? error.message : String(error) };
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=resource-versions.js.map
|