agentic-workflow-manager 9.16.1 → 9.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/commands/env/index.js +192 -0
- package/dist/src/commands/preflight/checks.js +63 -13
- package/dist/src/commands/registry/doctor.js +428 -0
- package/dist/src/commands/registry/index.js +52 -0
- package/dist/src/commands/registry/scaffold.js +140 -0
- package/dist/src/commands/sensors/bootstrap.js +2 -1
- package/dist/src/commands/sensors/compatibility/contract.js +30 -26
- package/dist/src/commands/sensors/compatibility/discovery.js +21 -0
- package/dist/src/commands/sensors/compatibility/manifest.js +169 -17
- package/dist/src/commands/sensors/compatibility/materialize.js +30 -5
- package/dist/src/commands/sensors/compatibility/source.js +3 -2
- package/dist/src/commands/sensors/coverage/render.js +8 -2
- package/dist/src/commands/sensors/coverage/resolve.js +7 -0
- package/dist/src/commands/sensors/init.js +15 -2
- package/dist/src/commands/sensors/migrate.js +13 -10
- package/dist/src/commands/sensors/prepare.js +53 -6
- package/dist/src/commands/sensors/project.js +60 -6
- package/dist/src/commands/sensors/result.js +18 -3
- package/dist/src/commands/sensors/run.js +124 -16
- package/dist/src/commands/sensors/status.js +163 -18
- package/dist/src/commands/watch/index.js +3 -0
- package/dist/src/core/environments/resolve.js +122 -0
- package/dist/src/core/environments/store.js +323 -0
- package/dist/src/core/facts/catalog.js +138 -0
- package/dist/src/core/facts/needs.js +57 -0
- package/dist/src/core/facts/types.js +21 -0
- package/dist/src/core/init/steps.js +5 -1
- package/dist/src/index.js +2 -0
- package/dist/tests/commands/env/index.test.js +261 -0
- package/dist/tests/commands/preflight/preflight.test.js +121 -0
- package/dist/tests/commands/registry/doctor.test.js +283 -0
- package/dist/tests/commands/registry/scaffold.test.js +186 -0
- package/dist/tests/commands/sensors/bootstrap.test.js +10 -0
- package/dist/tests/commands/sensors/compatibility/manifest.test.js +204 -0
- package/dist/tests/commands/sensors/init.test.js +107 -0
- package/dist/tests/commands/sensors/migrate.test.js +4 -3
- package/dist/tests/commands/sensors/partial-pack-bootstrap.test.js +1 -1
- package/dist/tests/commands/sensors/prepare.test.js +62 -0
- package/dist/tests/commands/sensors/result.test.js +66 -0
- package/dist/tests/commands/sensors/run.test.js +331 -0
- package/dist/tests/commands/sensors/status.test.js +204 -0
- package/dist/tests/commands/watch/reopened-196.test.js +9 -2
- package/dist/tests/core/environments/resolve.test.js +347 -0
- package/dist/tests/core/environments/store.test.js +248 -0
- package/dist/tests/core/facts/catalog.test.js +179 -0
- package/dist/tests/core/facts/needs.test.js +88 -0
- package/dist/tests/structural/native-release-artifacts.test.js +4 -2
- package/dist/tests/structural/operator-environments-docs-contract.test.js +70 -0
- package/dist/tests/structural/project-declared-sensors-docs-contract.test.js +104 -0
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/secure_fs.node +0 -0
- package/prebuilds/darwin-x64/secure_fs.node +0 -0
- package/prebuilds/win32-arm64/secure_fs.node +0 -0
- package/prebuilds/win32-x64/secure_fs.node +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.mergeFactDefinitions = mergeFactDefinitions;
|
|
7
|
+
exports.registerEnvCommand = registerEnvCommand;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const readline_1 = __importDefault(require("readline"));
|
|
11
|
+
const catalog_1 = require("../../core/facts/catalog");
|
|
12
|
+
const registries_1 = require("../../core/registries");
|
|
13
|
+
const store_1 = require("../../core/environments/store");
|
|
14
|
+
const resolve_1 = require("../../core/environments/resolve");
|
|
15
|
+
/**
|
|
16
|
+
* Merge facts catalogs from installed registry roots.
|
|
17
|
+
* Later roots may refine a fact, but must not weaken durability: volatile wins
|
|
18
|
+
* over durable so an expired secret cannot be revived by a durable override.
|
|
19
|
+
*/
|
|
20
|
+
function mergeFactDefinitions(into, from) {
|
|
21
|
+
for (const [id, next] of Object.entries(from)) {
|
|
22
|
+
const prev = Object.hasOwn(into, id) ? into[id] : undefined;
|
|
23
|
+
if (!prev) {
|
|
24
|
+
into[id] = next;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (prev.durability === 'volatile' && next.durability === 'durable') {
|
|
28
|
+
const kept = { ...next, durability: 'volatile' };
|
|
29
|
+
if (prev.ttl !== undefined)
|
|
30
|
+
kept.ttl = prev.ttl;
|
|
31
|
+
into[id] = kept;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
into[id] = next;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function loadCatalogFromInstalledRoots() {
|
|
38
|
+
const merged = { schemaVersion: 1, facts: {} };
|
|
39
|
+
for (const root of (0, registries_1.contentRoots)()) {
|
|
40
|
+
const factsPath = path_1.default.join(root, 'facts.json');
|
|
41
|
+
if (!fs_1.default.existsSync(factsPath))
|
|
42
|
+
continue;
|
|
43
|
+
const catalog = (0, catalog_1.parseFactsCatalog)(factsPath);
|
|
44
|
+
mergeFactDefinitions(merged.facts, catalog.facts);
|
|
45
|
+
}
|
|
46
|
+
return merged;
|
|
47
|
+
}
|
|
48
|
+
function defaultConsultedRoots() {
|
|
49
|
+
return (0, registries_1.contentRoots)();
|
|
50
|
+
}
|
|
51
|
+
function requireValue(value, flagHint) {
|
|
52
|
+
if (value === undefined) {
|
|
53
|
+
throw new store_1.EnvironmentStoreError(`${flagHint} requires --value <text> in non-interactive mode (prompt / editable store / --value only; RF-3.3)`);
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
async function defaultPromptValue(question) {
|
|
58
|
+
const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
59
|
+
try {
|
|
60
|
+
return await new Promise((resolve) => {
|
|
61
|
+
rl.question(question, (answer) => resolve(answer));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
rl.close();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function resolveValue(optsValue, flagHint, factId, deps) {
|
|
69
|
+
if (optsValue !== undefined)
|
|
70
|
+
return optsValue;
|
|
71
|
+
if (!deps.stdinIsTTY()) {
|
|
72
|
+
return requireValue(undefined, flagHint);
|
|
73
|
+
}
|
|
74
|
+
return Promise.resolve(deps.promptValue(`Value for ${factId}: `));
|
|
75
|
+
}
|
|
76
|
+
function setFact(factId, rawValue, deps) {
|
|
77
|
+
const catalog = deps.loadCatalog();
|
|
78
|
+
const name = (0, store_1.getActiveEnvironmentName)();
|
|
79
|
+
(0, store_1.writeFact)(name, factId, {
|
|
80
|
+
value: rawValue,
|
|
81
|
+
status: 'fresh',
|
|
82
|
+
updatedAt: new Date().toISOString(),
|
|
83
|
+
}, {
|
|
84
|
+
catalog,
|
|
85
|
+
consultedRoots: deps.consultedRoots(),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function registerEnvCommand(program, deps = {}) {
|
|
89
|
+
const loadCatalog = deps.loadCatalog ?? loadCatalogFromInstalledRoots;
|
|
90
|
+
const consultedRoots = deps.consultedRoots ?? defaultConsultedRoots;
|
|
91
|
+
const now = deps.now ?? (() => new Date());
|
|
92
|
+
const stdinIsTTY = deps.stdinIsTTY ?? (() => Boolean(process.stdin.isTTY));
|
|
93
|
+
const promptValue = deps.promptValue ?? defaultPromptValue;
|
|
94
|
+
const env = program.command('env').description('manage machine-local operator environments (facts store)');
|
|
95
|
+
env.command('status')
|
|
96
|
+
.description('show active environment and fact statuses vs catalog')
|
|
97
|
+
.action(() => {
|
|
98
|
+
const name = (0, store_1.getActiveEnvironmentName)();
|
|
99
|
+
const catalog = loadCatalog();
|
|
100
|
+
const store = (0, store_1.readValues)(name);
|
|
101
|
+
const statuses = (0, resolve_1.statusFor)(catalog, store, now());
|
|
102
|
+
const lines = [
|
|
103
|
+
`Active environment: ${name}`,
|
|
104
|
+
`Store: ${(0, store_1.valuesPath)(name)}`,
|
|
105
|
+
'',
|
|
106
|
+
'Facts:',
|
|
107
|
+
];
|
|
108
|
+
const ids = Object.keys(catalog.facts).sort();
|
|
109
|
+
if (ids.length === 0) {
|
|
110
|
+
lines.push(' (no facts in consulted catalogs)');
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
for (const id of ids) {
|
|
114
|
+
lines.push(` ${id}: ${statuses[id]}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
118
|
+
});
|
|
119
|
+
env.command('set <factId>')
|
|
120
|
+
.description('set a fact value in the active environment (--value for non-interactive)')
|
|
121
|
+
.option('--value <text>', 'fact value (required non-interactive; never runs registry commands)')
|
|
122
|
+
.action(async (factId, opts) => {
|
|
123
|
+
const value = await resolveValue(opts.value, 'awm env set', factId, {
|
|
124
|
+
stdinIsTTY,
|
|
125
|
+
promptValue,
|
|
126
|
+
});
|
|
127
|
+
setFact(factId, value, { loadCatalog, consultedRoots });
|
|
128
|
+
process.stdout.write(`Set ${factId} in environment ${(0, store_1.getActiveEnvironmentName)()}\n`);
|
|
129
|
+
});
|
|
130
|
+
env.command('edit')
|
|
131
|
+
.description('print the absolute path of the active environment values.json')
|
|
132
|
+
.action(() => {
|
|
133
|
+
const name = (0, store_1.getActiveEnvironmentName)();
|
|
134
|
+
const file = (0, store_1.valuesPath)(name);
|
|
135
|
+
process.stdout.write(`${file}\n`);
|
|
136
|
+
});
|
|
137
|
+
env.command('refresh [factId]')
|
|
138
|
+
.description('re-set a fact value (prompt / --value only; no registry commands)')
|
|
139
|
+
.option('--value <text>', 'new value (required non-interactive)')
|
|
140
|
+
.option('--all', 'refresh every fact present in the store (interactive prompts, or pass factId)')
|
|
141
|
+
.action(async (factId, opts) => {
|
|
142
|
+
if (opts.all && !factId) {
|
|
143
|
+
if (!stdinIsTTY()) {
|
|
144
|
+
throw new store_1.EnvironmentStoreError('awm env refresh --all requires a TTY (or pass <factId> with --value in non-interactive mode)');
|
|
145
|
+
}
|
|
146
|
+
const name = (0, store_1.getActiveEnvironmentName)();
|
|
147
|
+
const ids = Object.keys((0, store_1.readValues)(name));
|
|
148
|
+
if (ids.length === 0) {
|
|
149
|
+
throw new store_1.EnvironmentStoreError('awm env refresh --all: active store has no facts');
|
|
150
|
+
}
|
|
151
|
+
for (const id of ids) {
|
|
152
|
+
const value = await Promise.resolve(promptValue(`Value for ${id}: `));
|
|
153
|
+
setFact(id, value, { loadCatalog, consultedRoots });
|
|
154
|
+
process.stdout.write(`Refreshed ${id} in environment ${(0, store_1.getActiveEnvironmentName)()}\n`);
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (!factId) {
|
|
159
|
+
throw new store_1.EnvironmentStoreError('awm env refresh requires <factId> (or interactive --all)');
|
|
160
|
+
}
|
|
161
|
+
const value = await resolveValue(opts.value, 'awm env refresh', factId, {
|
|
162
|
+
stdinIsTTY,
|
|
163
|
+
promptValue,
|
|
164
|
+
});
|
|
165
|
+
setFact(factId, value, { loadCatalog, consultedRoots });
|
|
166
|
+
process.stdout.write(`Refreshed ${factId} in environment ${(0, store_1.getActiveEnvironmentName)()}\n`);
|
|
167
|
+
});
|
|
168
|
+
env.command('use <name>')
|
|
169
|
+
.description('switch the active environment (one active per machine)')
|
|
170
|
+
.action((name) => {
|
|
171
|
+
(0, store_1.setActiveEnvironmentName)(name);
|
|
172
|
+
process.stdout.write(`Active environment: ${name}\n`);
|
|
173
|
+
});
|
|
174
|
+
env.command('inject')
|
|
175
|
+
.description('print JSON injection payload for the active environment (default mode: reference)')
|
|
176
|
+
.option('--mode <mode>', 'injection mode: reference|context', 'reference')
|
|
177
|
+
.action((opts) => {
|
|
178
|
+
const rawMode = opts.mode ?? 'reference';
|
|
179
|
+
const mode = rawMode === 'reference' || rawMode === 'context' ? rawMode : 'reference';
|
|
180
|
+
const name = (0, store_1.getActiveEnvironmentName)();
|
|
181
|
+
const catalog = loadCatalog();
|
|
182
|
+
const store = (0, store_1.readValues)(name);
|
|
183
|
+
const result = (0, resolve_1.buildInjection)({
|
|
184
|
+
catalog,
|
|
185
|
+
store,
|
|
186
|
+
storePath: (0, store_1.valuesPath)(name),
|
|
187
|
+
inject: mode,
|
|
188
|
+
now: now(),
|
|
189
|
+
});
|
|
190
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
191
|
+
});
|
|
192
|
+
}
|
|
@@ -100,13 +100,18 @@ function checkContextKernel(cwd) {
|
|
|
100
100
|
remedy: inspection.remedy,
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
|
+
function isDisabledCheckDetail(detail) {
|
|
104
|
+
return detail === 'disabled' || detail.startsWith('disabled ');
|
|
105
|
+
}
|
|
103
106
|
function manifestView(status) {
|
|
104
|
-
|
|
107
|
+
// Only-project READY has pack null but non-empty checks — still a valid view (RF-2.8).
|
|
108
|
+
// Do not invent a fake pack id; keep pack null and surface sensors from status.checks.
|
|
109
|
+
if (status.mode !== 'project-sensors' && status.mode !== 'legacy-v1')
|
|
105
110
|
return null;
|
|
106
111
|
const sensors = Object.fromEntries(Object.entries(status.checks).map(([name, check]) => [name, {
|
|
107
|
-
enabled: check.detail
|
|
112
|
+
enabled: !isDisabledCheckDetail(check.detail),
|
|
108
113
|
}]));
|
|
109
|
-
return { pack: status.pack, sensors };
|
|
114
|
+
return { pack: status.pack ?? null, sensors };
|
|
110
115
|
}
|
|
111
116
|
/**
|
|
112
117
|
* Total sensor entries and how many are enabled (`enabled !== false`, so a missing
|
|
@@ -139,12 +144,32 @@ function checkManifest(status) {
|
|
|
139
144
|
};
|
|
140
145
|
}
|
|
141
146
|
if (status.mode === 'invalid') {
|
|
147
|
+
if (status.reason === 'schema-unsupported') {
|
|
148
|
+
return {
|
|
149
|
+
id: 'manifest',
|
|
150
|
+
ok: false,
|
|
151
|
+
detail: '.awm/sensors.json uses an unsupported schema',
|
|
152
|
+
remedy: 'fix or regenerate it with `awm sensors init`',
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// JSON.parse succeeded but schema/entry validation failed (RF-4.1/4.2).
|
|
156
|
+
// Never claim "not valid JSON"; name the sensor/field from status.reason.
|
|
157
|
+
if (typeof status.reason === 'string' && (status.reason.startsWith('schema-invalid') || status.reason.includes('Invalid sensor manifest'))) {
|
|
158
|
+
const detail = status.reason.startsWith('schema-invalid:')
|
|
159
|
+
? status.reason.slice('schema-invalid:'.length).trim()
|
|
160
|
+
: status.reason;
|
|
161
|
+
// RF-3.3: project schema invalidity must not lead with `awm sensors init`.
|
|
162
|
+
return {
|
|
163
|
+
id: 'manifest',
|
|
164
|
+
ok: false,
|
|
165
|
+
detail,
|
|
166
|
+
remedy: 'repair the invalid sensor entry in .awm/sensors.json',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
142
169
|
return {
|
|
143
170
|
id: 'manifest',
|
|
144
171
|
ok: false,
|
|
145
|
-
detail:
|
|
146
|
-
? '.awm/sensors.json uses an unsupported schema'
|
|
147
|
-
: '.awm/sensors.json is not valid JSON',
|
|
172
|
+
detail: '.awm/sensors.json is not valid JSON',
|
|
148
173
|
remedy: 'fix or regenerate it with `awm sensors init`',
|
|
149
174
|
};
|
|
150
175
|
}
|
|
@@ -165,6 +190,14 @@ function checkManifest(status) {
|
|
|
165
190
|
// for the detected stack, so `awm sensors init` built an honest, empty manifest
|
|
166
191
|
// rather than inventing defaults. That must not read as "all sensors disabled".
|
|
167
192
|
if (total === 0) {
|
|
193
|
+
if (manifest.pack === null) {
|
|
194
|
+
return {
|
|
195
|
+
id: 'manifest',
|
|
196
|
+
ok: false,
|
|
197
|
+
detail: 'only-project manifest has no sensors',
|
|
198
|
+
remedy: 'add source:"project" sensor entries to .awm/sensors.json, or declare a pack',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
168
201
|
return {
|
|
169
202
|
id: 'manifest',
|
|
170
203
|
ok: false,
|
|
@@ -179,12 +212,14 @@ function checkManifest(status) {
|
|
|
179
212
|
// gap is one missing tool could then configure sensors and still never pass.
|
|
180
213
|
const uninitialized = Object.keys(status.uninitialized ?? {});
|
|
181
214
|
const gap = uninitialized.length === 0 ? '' : `; ${uninitialized.length} applicable sensor(s) not initialized: ${uninitialized.join(', ')}`;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
215
|
+
const summary = manifest.pack === null
|
|
216
|
+
? (enabled === 0
|
|
217
|
+
? `only-project, all ${total} sensors disabled (deliberate opt-out)`
|
|
218
|
+
: `only-project, ${enabled}/${total} project-declared sensors enabled`)
|
|
219
|
+
: (enabled === 0
|
|
220
|
+
? `pack ${manifest.pack}, all ${total} sensors disabled (deliberate opt-out)`
|
|
221
|
+
: `pack ${manifest.pack}, ${enabled}/${total} sensors enabled`);
|
|
222
|
+
return { id: 'manifest', ok: true, detail: summary + gap };
|
|
188
223
|
}
|
|
189
224
|
/** Every enabled sensor's command must resolve. This is the check nothing was calling. */
|
|
190
225
|
async function checkTools(cwd, status) {
|
|
@@ -198,11 +233,14 @@ async function checkTools(cwd, status) {
|
|
|
198
233
|
// already guards against; this defends the invariant independently rather than
|
|
199
234
|
// relying solely on `checkManifest`'s gate to catch this exact manifest shape.
|
|
200
235
|
if (Object.keys(status.checks).length === 0) {
|
|
236
|
+
const packId = typeof status.pack === 'string' && status.pack.length > 0 ? status.pack : null;
|
|
201
237
|
return {
|
|
202
238
|
id: 'tools',
|
|
203
239
|
ok: false,
|
|
204
240
|
detail: 'no sensors configured to check (0 sensor entries in the manifest)',
|
|
205
|
-
remedy:
|
|
241
|
+
remedy: packId === null
|
|
242
|
+
? 'add source:"project" sensor entries to .awm/sensors.json, or declare a pack'
|
|
243
|
+
: `registry has no pack for '${packId}': run \`awm update\` or add a registry that has it`,
|
|
206
244
|
};
|
|
207
245
|
}
|
|
208
246
|
const broken = Object.entries(status.checks).filter(([, c]) => !c.ok);
|
|
@@ -227,6 +265,9 @@ function checkPack(cwd, status) {
|
|
|
227
265
|
const manifest = manifestView(status);
|
|
228
266
|
if (!manifest)
|
|
229
267
|
return { id: 'pack', ok: true, detail: 'skipped (no manifest)' };
|
|
268
|
+
if (manifest.pack === null) {
|
|
269
|
+
return { id: 'pack', ok: true, detail: 'no pack (project-declared sensors only)' };
|
|
270
|
+
}
|
|
230
271
|
const detection = (0, detection_1.detectStack)(cwd);
|
|
231
272
|
if (manifest.pack === 'generic' && detection.pack !== 'generic') {
|
|
232
273
|
return {
|
|
@@ -327,6 +368,15 @@ async function checkSensorExecution(cwd) {
|
|
|
327
368
|
try {
|
|
328
369
|
const output = await (0, run_1.runSensors)({ cwd, all: true });
|
|
329
370
|
if (output.overall === 'pass') {
|
|
371
|
+
const invalid = output.invalidEntries ?? [];
|
|
372
|
+
if (invalid.length > 0) {
|
|
373
|
+
return {
|
|
374
|
+
id: 'sensors-execution',
|
|
375
|
+
ok: false,
|
|
376
|
+
detail: `sensors passed but ${invalid.length} invalidEntries remain: ${invalid.map(entry => `${entry.name} (${entry.reason})`).join('; ')}`,
|
|
377
|
+
remedy: 'repair the invalid sensor entries in .awm/sensors.json, then rerun `awm preflight --verify-sensors`',
|
|
378
|
+
};
|
|
379
|
+
}
|
|
330
380
|
return { id: 'sensors-execution', ok: true, detail: 'all selected sensors completed with pass' };
|
|
331
381
|
}
|
|
332
382
|
return {
|