@aiwg/cli 2026.10.6 → 2026.10.8
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/bin/aiwg.mjs +4 -2
- package/dist/src/a2a/agent-card.js +11 -0
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
- package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
- package/dist/src/artifacts/fortemi-shard-export.js +284 -0
- package/dist/src/artifacts/index-preparation.js +2 -0
- package/dist/src/auth/client.js +5 -1
- package/dist/src/auth/credential-store.js +5 -1
- package/dist/src/auth/sensitive-input.js +151 -0
- package/dist/src/channel/manager.mjs +3 -0
- package/dist/src/cli/handlers/agentcard.js +47 -25
- package/dist/src/cli/handlers/auth.js +22 -2
- package/dist/src/cli/handlers/cost-report.js +7 -1
- package/dist/src/cli/handlers/decision.js +36 -10
- package/dist/src/cli/handlers/effect.js +4 -84
- package/dist/src/cli/handlers/guided-setup.js +1 -1
- package/dist/src/cli/handlers/init.js +1 -1
- package/dist/src/cli/handlers/install.js +6 -0
- package/dist/src/cli/handlers/local-executor.js +33 -6
- package/dist/src/cli/handlers/marketplace.js +15 -4
- package/dist/src/cli/handlers/setup.js +24 -20
- package/dist/src/cli/handlers/use.js +57 -15
- package/dist/src/cli/prompt-utils.js +73 -0
- package/dist/src/config/gitignore.js +4 -0
- package/dist/src/config/workspace.js +2 -1
- package/dist/src/cost/fleet-report.js +20 -22
- package/dist/src/decision/adapters/jev.js +20 -31
- package/dist/src/decision/ask.js +6 -1
- package/dist/src/decision/benchmark.js +19 -6
- package/dist/src/decision/driver.js +2 -2
- package/dist/src/decision/jev-credentials.js +18 -5
- package/dist/src/decision/ledger.js +230 -34
- package/dist/src/decision/qualification/gate-evidence.js +6 -4
- package/dist/src/decision/qualification/load-staged.js +47 -10
- package/dist/src/decision/qualification/runner.js +7 -3
- package/dist/src/discovery/interview.js +10 -5
- package/dist/src/discovery/plan-apply.js +26 -30
- package/dist/src/discovery/plan-validation.js +2 -2
- package/dist/src/effects/host.js +91 -0
- package/dist/src/extensions/commands/definitions.js +2 -2
- package/dist/src/extensions/project-local-gitignore.js +2 -0
- package/dist/src/issues/cli.js +16 -5
- package/dist/src/issues/live.js +44 -12
- package/dist/src/marketplace/exchange.js +18 -6
- package/dist/src/marketplace/provenance.js +3 -2
- package/dist/src/mcp/adapters/codex-runtime.js +46 -76
- package/dist/src/mcp/cli.mjs +122 -109
- package/dist/src/mcp/credentials.d.mts +4 -0
- package/dist/src/mcp/credentials.mjs +44 -3
- package/dist/src/mcp/grok-build-config.mjs +4 -1
- package/dist/src/mcp/muse-native-config.mjs +2 -1
- package/dist/src/mcp/omp-config.mjs +2 -0
- package/dist/src/mcp/registry.js +19 -15
- package/dist/src/mcp/registry.mjs +2 -18
- package/dist/src/mcp/tools/agentic-sandbox.mjs +46 -28
- package/dist/src/mcp/tools/worker-invoke.mjs +13 -2
- package/dist/src/memory/project-registry.js +2 -1
- package/dist/src/mission-protocol/worker-status.js +91 -1
- package/dist/src/models/grok-build-models.js +20 -0
- package/dist/src/models/worker-installation.d.mts +1 -1
- package/dist/src/models/worker-installation.mjs +65 -4
- package/dist/src/packages/adapters/git.js +99 -23
- package/dist/src/packages/adapters/gitea.js +8 -27
- package/dist/src/packages/adapters/github.js +7 -25
- package/dist/src/packages/registry.js +4 -4
- package/dist/src/process/cli-command.d.mts +5 -0
- package/dist/src/process/cli-command.mjs +42 -0
- package/dist/src/providers/provider-definitions.js +2 -2
- package/dist/src/serve/shared-host-scheduler.js +4 -1
- package/dist/src/serve/worker-invocation.js +10 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +9 -9
- package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
- package/dist/src/smiths/context-pipeline/finalization.js +3 -2
- package/dist/src/smiths/context-pipeline/generator.js +7 -19
- package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
- package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +4 -12
- package/dist/src/storage/backends/fortemi.js +107 -49
- package/dist/src/tracker/project-inputs.js +2 -0
- package/dist/src/update/notifier.mjs +2 -4
- package/package.json +1 -1
- package/tools/agents/providers/base.mjs +5 -1
- package/tools/agents/providers/claude.mjs +5 -2
- package/tools/agents/providers/codex.mjs +4 -1
- package/tools/agents/providers/context-write.mjs +28 -0
- package/tools/agents/providers/copilot.mjs +5 -2
- package/tools/agents/providers/hermes.mjs +5 -2
- package/tools/agents/providers/omp.mjs +3 -1
- package/tools/agents/providers/rule-delivery.mjs +29 -12
- package/tools/agents/providers/warp.mjs +4 -1
- package/tools/agents/providers/windsurf.mjs +6 -3
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
- package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
- package/tools/ralph-external/process-monitor.mjs +42 -4
- package/tools/ralph-external/session-launcher.mjs +125 -3
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* @issue #1253
|
|
11
11
|
*/
|
|
12
12
|
import { fetchAgentCard } from '../../a2a/agent-card.js';
|
|
13
|
+
import { resolveSensitiveInput, sensitiveInputOptions } from '../../auth/sensitive-input.js';
|
|
13
14
|
import { loadJwkSet } from '../../a2a/jws.js';
|
|
14
15
|
import { AiwgError, EXIT_CODES, handlerResultFromError } from '../errors.js';
|
|
15
16
|
function parseVerifyArgs(args) {
|
|
@@ -39,6 +40,9 @@ function parseVerifyArgs(args) {
|
|
|
39
40
|
}
|
|
40
41
|
const jwks = get('--jwks');
|
|
41
42
|
const bearer = get('--bearer');
|
|
43
|
+
if (args.includes('--bearer') && (!bearer || bearer.startsWith('--'))) {
|
|
44
|
+
throw new AiwgError({ code: 'ERR_AGENTCARD_BEARER', message: '--bearer requires a value; prefer --bearer-stdin, --bearer-file, --bearer-store or --bearer-env.', exitCode: EXIT_CODES.USAGE });
|
|
45
|
+
}
|
|
42
46
|
const skipVerify = args.includes('--skip-verify');
|
|
43
47
|
const json = args.includes('--json');
|
|
44
48
|
if (!jwks && !skipVerify) {
|
|
@@ -73,36 +77,49 @@ async function loadJwksFromSource(source) {
|
|
|
73
77
|
}
|
|
74
78
|
async function handleVerify(args) {
|
|
75
79
|
const parsed = parseVerifyArgs(args);
|
|
80
|
+
const sources = sensitiveInputOptions(args, 'bearer');
|
|
81
|
+
const hasSource = sources.interactive || sources.stdin || sources.file !== undefined || sources.store !== undefined || sources.env !== undefined;
|
|
82
|
+
if (parsed.bearer !== undefined) {
|
|
83
|
+
console.error('Warning: --bearer is deprecated and exposes the value on argv; use --bearer-interactive, --bearer-stdin, --bearer-file, --bearer-store or --bearer-env.');
|
|
84
|
+
}
|
|
85
|
+
// No source intentionally means public/unauthenticated access, even at a TTY.
|
|
86
|
+
const bearer = hasSource
|
|
87
|
+
? await resolveSensitiveInput(sources)
|
|
88
|
+
: parsed.bearer === undefined ? undefined : await resolveSensitiveInput({ prefix: 'bearer', env: 'LEGACY_BEARER' }, { readEnv: () => parsed.bearer });
|
|
89
|
+
const redact = (text) => bearer ? text.split(bearer).join('[REDACTED]') : text;
|
|
90
|
+
const log = (text) => console.log(redact(text));
|
|
91
|
+
const error = (text) => console.error(redact(text));
|
|
92
|
+
const jsonReplacer = (_key, value) => typeof value === 'string' ? redact(value) : value;
|
|
76
93
|
const opts = {
|
|
77
94
|
skipVerify: parsed.skipVerify,
|
|
78
95
|
};
|
|
79
|
-
if (
|
|
80
|
-
opts.bearer =
|
|
81
|
-
if (parsed.jwks !== undefined && !parsed.skipVerify) {
|
|
82
|
-
opts.jwks = await loadJwksFromSource(parsed.jwks);
|
|
83
|
-
}
|
|
96
|
+
if (bearer !== undefined)
|
|
97
|
+
opts.bearer = bearer;
|
|
84
98
|
let verified;
|
|
85
99
|
try {
|
|
100
|
+
if (parsed.jwks !== undefined && !parsed.skipVerify) {
|
|
101
|
+
opts.jwks = await loadJwksFromSource(parsed.jwks);
|
|
102
|
+
}
|
|
86
103
|
verified = await fetchAgentCard(parsed.host, parsed.instance, opts);
|
|
87
104
|
}
|
|
88
105
|
catch (err) {
|
|
89
106
|
const message = err.message;
|
|
90
107
|
if (parsed.json) {
|
|
91
|
-
|
|
108
|
+
log(JSON.stringify({
|
|
92
109
|
ok: false,
|
|
93
110
|
host: parsed.host,
|
|
94
111
|
instance: parsed.instance,
|
|
95
112
|
error: message,
|
|
96
|
-
},
|
|
113
|
+
}, jsonReplacer, 2));
|
|
97
114
|
}
|
|
98
115
|
else {
|
|
99
|
-
|
|
100
|
-
|
|
116
|
+
error(`AgentCard verification FAILED for ${parsed.host}/${parsed.instance}`);
|
|
117
|
+
error(` ${message}`);
|
|
101
118
|
}
|
|
102
119
|
return { exitCode: EXIT_CODES.GENERAL };
|
|
103
120
|
}
|
|
104
121
|
if (parsed.json) {
|
|
105
|
-
|
|
122
|
+
log(JSON.stringify({
|
|
106
123
|
ok: true,
|
|
107
124
|
host: parsed.host,
|
|
108
125
|
instance: parsed.instance,
|
|
@@ -118,38 +135,38 @@ async function handleVerify(args) {
|
|
|
118
135
|
capabilities: verified.card.capabilities,
|
|
119
136
|
skills: verified.card.skills,
|
|
120
137
|
},
|
|
121
|
-
},
|
|
138
|
+
}, jsonReplacer, 2));
|
|
122
139
|
}
|
|
123
140
|
else {
|
|
124
141
|
const banner = parsed.skipVerify ? '(verification SKIPPED)' : 'VERIFIED';
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
log(`AgentCard ${banner} for ${parsed.host}/${parsed.instance}`);
|
|
143
|
+
log(` name: ${verified.card.name}`);
|
|
144
|
+
log(` protocolVersion: ${verified.card.protocolVersion}`);
|
|
145
|
+
log(` url: ${verified.card.url}`);
|
|
146
|
+
log(` version: ${verified.card.version}`);
|
|
130
147
|
if (verified.card.preferredTransport) {
|
|
131
|
-
|
|
148
|
+
log(` preferredTransport: ${verified.card.preferredTransport}`);
|
|
132
149
|
}
|
|
133
150
|
if (verified.kid) {
|
|
134
|
-
|
|
151
|
+
log(` signing kid: ${verified.kid}`);
|
|
135
152
|
}
|
|
136
|
-
|
|
153
|
+
log(` verifiedAt: ${verified.verifiedAt}`);
|
|
137
154
|
const exts = verified.card.capabilities?.extensions ?? [];
|
|
138
155
|
if (exts.length > 0) {
|
|
139
|
-
|
|
156
|
+
log(` extensions:`);
|
|
140
157
|
for (const e of exts) {
|
|
141
158
|
const tag = e.required ? '[required]' : '[optional]';
|
|
142
|
-
|
|
159
|
+
log(` ${tag} ${e.uri}`);
|
|
143
160
|
}
|
|
144
161
|
}
|
|
145
162
|
const skills = verified.card.skills ?? [];
|
|
146
163
|
if (skills.length > 0) {
|
|
147
|
-
|
|
164
|
+
log(` skills (${skills.length}):`);
|
|
148
165
|
for (const s of skills.slice(0, 5)) {
|
|
149
|
-
|
|
166
|
+
log(` - ${s.id}${s.name ? ' (' + s.name + ')' : ''}`);
|
|
150
167
|
}
|
|
151
168
|
if (skills.length > 5)
|
|
152
|
-
|
|
169
|
+
log(` ... and ${skills.length - 5} more`);
|
|
153
170
|
}
|
|
154
171
|
}
|
|
155
172
|
return { exitCode: 0 };
|
|
@@ -164,7 +181,12 @@ function printUsage() {
|
|
|
164
181
|
console.log(' --host <url> Executor host (e.g. https://exec.test)');
|
|
165
182
|
console.log(' --instance <id> Instance id (UUIDv7)');
|
|
166
183
|
console.log(' --jwks <path|url> JWKS file path or URL (required unless --skip-verify)');
|
|
167
|
-
console.log(' --bearer
|
|
184
|
+
console.log(' --bearer-interactive Read optional bearer in a terminal without echo');
|
|
185
|
+
console.log(' --bearer-stdin Read optional bearer from piped stdin');
|
|
186
|
+
console.log(' --bearer-file <path> Read optional bearer from an owner-only private file');
|
|
187
|
+
console.log(' --bearer-store <name> Read optional bearer from a named OS store entry');
|
|
188
|
+
console.log(' --bearer-env <NAME> Read optional bearer from a named shell variable');
|
|
189
|
+
console.log(' --bearer <token> Deprecated: exposes the value on argv');
|
|
168
190
|
console.log(' --skip-verify Skip JWS verification (NOT recommended)');
|
|
169
191
|
console.log(' --json Emit JSON output');
|
|
170
192
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { namedSensitiveStore, resolveSensitiveInput, sensitiveInputOptions } from '../../auth/sensitive-input.js';
|
|
1
2
|
import { AuthClient, defaultBrowserOpener } from "../../auth/client.js";
|
|
2
3
|
import { authConfigFromEnvironment } from "../../auth/config.js";
|
|
3
4
|
import { createCredentialStore } from "../../auth/credential-store.js";
|
|
@@ -5,6 +6,8 @@ function usage() {
|
|
|
5
6
|
return [
|
|
6
7
|
"Usage:",
|
|
7
8
|
" aiwg auth login [--device] [--device-label <label>] [--store native|file] [--allow-file-store]",
|
|
9
|
+
" aiwg auth secret set <name> [--value-interactive | --value-stdin | --value-file <path> | --value-store <name> | --value-env <name>]",
|
|
10
|
+
" aiwg auth secret delete <name>",
|
|
8
11
|
" aiwg auth status [--json] [--store native|file] [--allow-file-store]",
|
|
9
12
|
" aiwg auth logout [--all] [--store native|file] [--allow-file-store]",
|
|
10
13
|
"",
|
|
@@ -46,9 +49,25 @@ export function createAuthHandler(dependencies = {}) {
|
|
|
46
49
|
return { exitCode: 0 };
|
|
47
50
|
}
|
|
48
51
|
const [subcommand, ...args] = ctx.args;
|
|
49
|
-
if (!["login", "status", "logout"].includes(subcommand))
|
|
52
|
+
if (!["login", "status", "logout", "secret"].includes(subcommand))
|
|
50
53
|
return { exitCode: 2, message: usage() };
|
|
51
54
|
try {
|
|
55
|
+
if (subcommand === "secret") {
|
|
56
|
+
const [operation, name, ...inputArgs] = args;
|
|
57
|
+
if (!["set", "delete"].includes(operation) || !name || name.startsWith('--'))
|
|
58
|
+
return { exitCode: 2, message: usage() };
|
|
59
|
+
const store = (dependencies.secretStore ?? namedSensitiveStore)(name);
|
|
60
|
+
if (operation === "delete")
|
|
61
|
+
await store.deleteSecret();
|
|
62
|
+
else {
|
|
63
|
+
const secret = await resolveSensitiveInput({ ...sensitiveInputOptions(inputArgs), signal: ctx.signal }, {
|
|
64
|
+
environment: dependencies.env, ...dependencies.sensitiveInput,
|
|
65
|
+
});
|
|
66
|
+
await store.saveSecret(secret);
|
|
67
|
+
}
|
|
68
|
+
console.log(operation === "delete" ? "Named sensitive value removed." : "Named sensitive value stored.");
|
|
69
|
+
return { exitCode: 0 };
|
|
70
|
+
}
|
|
52
71
|
const storeMode = value(args, "--store") || "native";
|
|
53
72
|
if (!["native", "file"].includes(storeMode))
|
|
54
73
|
return { exitCode: 2, message: "--store must be native or file" };
|
|
@@ -119,7 +138,8 @@ export function createAuthHandler(dependencies = {}) {
|
|
|
119
138
|
return { exitCode: 0 };
|
|
120
139
|
}
|
|
121
140
|
catch (error) {
|
|
122
|
-
return { exitCode: classify(error), message: error instanceof Error ? error.message :
|
|
141
|
+
return { exitCode: classify(error), message: subcommand === 'secret' && !/^(--value-|A sensitive value|An interactive terminal|Sensitive value store name)/.test(error instanceof Error ? error.message : '')
|
|
142
|
+
? 'Native sensitive value store operation failed.' : error instanceof Error ? error.message : String(error) };
|
|
123
143
|
}
|
|
124
144
|
},
|
|
125
145
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** OpenRouter fleet cost-report CLI handler. @issue #1187 */
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { sensitiveInputOptions } from '../../auth/sensitive-input.js';
|
|
4
5
|
import { FleetConfigMissingError, formatFleetSpendReport, generateFleetSpendReport, } from '../../cost/fleet-report.js';
|
|
5
6
|
function option(args, name) {
|
|
6
7
|
const index = args.indexOf(name);
|
|
@@ -9,10 +10,11 @@ function option(args, name) {
|
|
|
9
10
|
}
|
|
10
11
|
function usage() {
|
|
11
12
|
return [
|
|
12
|
-
'Usage: aiwg cost-report (--fleet | --key <key_ref>) [--source openrouter] [--config <fleet.yaml>] [--json]',
|
|
13
|
+
'Usage: aiwg cost-report (--fleet | --key <key_ref> [--key-store <name>]) [--source openrouter] [--config <fleet.yaml>] [--json]',
|
|
13
14
|
'',
|
|
14
15
|
'Reads bot-to-key references from ~/.config/aiwg/fleet.yaml and credentials from',
|
|
15
16
|
'~/.config/aiwg/keys/<key_ref> or AIWG_OPENROUTER_KEY_<KEY_REF>.',
|
|
17
|
+
'Use --key-store <name> for one key, or per-entry key_store references in fleet YAML.',
|
|
16
18
|
'',
|
|
17
19
|
'AIWG observes and correlates spend; OpenRouter enforces all key limits and caps.',
|
|
18
20
|
].join('\n');
|
|
@@ -40,6 +42,9 @@ export const costReportHandler = {
|
|
|
40
42
|
? path.resolve(ctx.cwd, configuredPath)
|
|
41
43
|
: path.join(os.homedir(), '.config', 'aiwg', 'fleet.yaml');
|
|
42
44
|
try {
|
|
45
|
+
const keyStore = sensitiveInputOptions(ctx.args, 'key').store;
|
|
46
|
+
if (keyStore !== undefined && !keyRef)
|
|
47
|
+
return { exitCode: 2, message: '--key-store requires --key <key_ref>; use per-entry key_store references for --fleet.' };
|
|
43
48
|
const capValue = option(ctx.args, '--monthly-cap');
|
|
44
49
|
const cap = capValue === undefined ? 0 : Number(capValue);
|
|
45
50
|
if (!Number.isFinite(cap) || cap < 0)
|
|
@@ -53,6 +58,7 @@ export const costReportHandler = {
|
|
|
53
58
|
bot: option(ctx.args, '--bot') ?? keyRef,
|
|
54
59
|
machine: option(ctx.args, '--machine') ?? os.hostname(),
|
|
55
60
|
key_ref: keyRef,
|
|
61
|
+
...(keyStore !== undefined ? { key_store: keyStore } : {}),
|
|
56
62
|
monthly_cap: cap,
|
|
57
63
|
}],
|
|
58
64
|
} : {}),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { resolveSensitiveInput, sensitiveInputOptions } from '../../auth/sensitive-input.js';
|
|
1
2
|
import { decisionPricing } from '../../decision/pricing.js';
|
|
2
|
-
import { ATTRIBUTION_FIELD_PATTERN, DecisionAskUsageError, decisionCapabilities, DecisionLedgerSinceError, decisionLedgerHistory, decisionLedgerStats, evaluateRequestPath, listPatterns, livePlan, MAX_ATTRIBUTION_FIELD_LENGTH, materializeSyntheticClassificationSetup, parseDecisionLedgerSince, readDecisionContextFile, runDecisionOffloadCheckpoint, runOfflinePattern, setupJev, showPattern, syntheticClassificationSetup, validateDecisionInput, } from '../../decision/driver.js';
|
|
3
|
+
import { ATTRIBUTION_FIELD_PATTERN, DecisionAskUsageError, decisionCapabilities, DecisionLedgerSinceError, decisionLedgerHistory, decisionLedgerStats, rotateDecisionLedger, parseDecisionLedgerRetention, evaluateRequestPath, listPatterns, livePlan, MAX_ATTRIBUTION_FIELD_LENGTH, materializeSyntheticClassificationSetup, parseDecisionLedgerSince, readDecisionContextFile, runDecisionOffloadCheckpoint, runOfflinePattern, setupJev, showPattern, syntheticClassificationSetup, validateDecisionInput, } from '../../decision/driver.js';
|
|
3
4
|
function jsonResult(value, exitCode = 0) {
|
|
4
5
|
return { exitCode, message: JSON.stringify(value, null, 2), rawOutput: true };
|
|
5
6
|
}
|
|
6
7
|
function usage() {
|
|
7
8
|
return [
|
|
8
|
-
'Usage: aiwg decision <capabilities|status|patterns|validate|evaluate|setup|history|stats|pricing> [options]',
|
|
9
|
+
'Usage: aiwg decision <capabilities|status|patterns|validate|evaluate|setup|history|stats|rotate|pricing> [options]',
|
|
9
10
|
' aiwg decision ask --question "<q>" (--yes-no | --choices a,b,c | --scale 1-5) [--context <text> | --context-file <path> | --context-stdin] [--threshold 0.8] [--timeout-ms 15000] [--passes N] [--escalation-passes N] [--provider <id>] [--session <id>] [--caller <tag>] [--record-text] [--json]',
|
|
10
11
|
'',
|
|
11
12
|
'Commands:',
|
|
@@ -17,11 +18,12 @@ function usage() {
|
|
|
17
18
|
' validate <request|definition|ruleset|binding> <path>',
|
|
18
19
|
' evaluate --request <path> [--host-policy-module <path>]',
|
|
19
20
|
' setup synthetic-classification [--output-dir <dir>]',
|
|
20
|
-
' setup jev [--token-stdin] [--region <r>] [--endpoint <url>] [--verify] [--remove]',
|
|
21
|
+
' setup jev [--token-interactive | --token-stdin | --token-file <path> | --token-store <name> | --token-env <name>] [--region <r>] [--endpoint <url>] [--verify] [--remove]',
|
|
21
22
|
' ask --question "<q>" (--yes-no | --choices a,b,c | --scale 1-5) [--provider <id>] [--session <id>] [--caller <tag>] [--record-text]',
|
|
22
|
-
' history [--since <date>] [--provider <id>] [--session <id>] [--kind yes-no|choices|scale] [--status answered|fallback] [--caller <tag>|<prefix>* | --no-caller] [--json]',
|
|
23
|
+
' history [--since <date>] [--provider <id>] [--session <id>] [--kind yes-no|choices|scale] [--status answered|fallback] [--caller <tag>|<prefix>* | --no-caller] [--include-archives] [--json]',
|
|
23
24
|
' pricing [--refresh --model jev-X.Y.Z] [--dry-run] [--offline | --allow-snapshot] [--json]',
|
|
24
|
-
' stats [--since <date>] [--provider <id>] [--session <id>] [--kind yes-no|choices|scale] [--status answered|fallback] [--caller <tag>|<prefix>* | --no-caller] [--group-by provider|session|kind|caller|passes|escalation] [--json]',
|
|
25
|
+
' stats [--since <date>] [--provider <id>] [--session <id>] [--kind yes-no|choices|scale] [--status answered|fallback] [--caller <tag>|<prefix>* | --no-caller] [--group-by provider|session|kind|caller|passes|escalation] [--include-archives] [--json]',
|
|
26
|
+
' rotate [--keep-last <Nd|N> | --all] [--to <path>] [--json]',
|
|
25
27
|
'',
|
|
26
28
|
'Notes:',
|
|
27
29
|
' Every COMPLETED `ask` call (CLI and MCP) appends one record to the decision',
|
|
@@ -41,9 +43,13 @@ function usage() {
|
|
|
41
43
|
` --session and --caller must match ${ATTRIBUTION_FIELD_PATTERN.source} (<= ${MAX_ATTRIBUTION_FIELD_LENGTH} characters).`,
|
|
42
44
|
' `history`/`stats` accept `--caller <tag>` (exact), `--caller \'<prefix>*\'` (prefix; the only wildcard), or `--no-caller` (records with no tag; not combinable with `--caller`).',
|
|
43
45
|
' `history`/`stats` reject unknown flags and positional arguments with exit 2.',
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
46
|
+
' `rotate` keeps the last 90 days by default; --keep-last accepts days or a',
|
|
47
|
+
' positive count. Rotating everything requires --all. Malformed lines stay',
|
|
48
|
+
' inline unless --all is used. Archives are private .jsonl files beneath',
|
|
49
|
+
' archive/decision/ in the resolved artifact root; --to must stay there.',
|
|
50
|
+
' `history`/`stats` optionally read archives with --include-archives, offline.',
|
|
51
|
+
' Appenders, rotation and readers share one cross-process lock.',
|
|
52
|
+
' The ledger trusts only the local filesystem under the project',
|
|
47
53
|
' artifact root, owned by the same user running aiwg — it is not a defense',
|
|
48
54
|
' against a hostile process already running as that user.',
|
|
49
55
|
].join('\n');
|
|
@@ -101,7 +107,7 @@ function takeAttributionOption(args, name, reason) {
|
|
|
101
107
|
return value;
|
|
102
108
|
}
|
|
103
109
|
async function decisionSetupJev(rest) {
|
|
104
|
-
const token = rest.includes('--
|
|
110
|
+
const token = rest.includes('--remove') ? undefined : await resolveSensitiveInput(sensitiveInputOptions(rest, 'token'));
|
|
105
111
|
const result = await setupJev({
|
|
106
112
|
token,
|
|
107
113
|
region: takeOption(rest, '--region'),
|
|
@@ -206,7 +212,7 @@ function parseGroupBy(value) {
|
|
|
206
212
|
return value;
|
|
207
213
|
}
|
|
208
214
|
const HISTORY_VALUE_FLAGS = ['--since', '--provider', '--session', '--kind', '--status', '--caller'];
|
|
209
|
-
const HISTORY_BOOL_FLAGS = ['--json', '--no-caller'];
|
|
215
|
+
const HISTORY_BOOL_FLAGS = ['--json', '--no-caller', '--include-archives'];
|
|
210
216
|
const STATS_VALUE_FLAGS = [...HISTORY_VALUE_FLAGS, '--group-by'];
|
|
211
217
|
const STATS_BOOL_FLAGS = HISTORY_BOOL_FLAGS;
|
|
212
218
|
/**
|
|
@@ -268,6 +274,7 @@ function parseLedgerFilters(args) {
|
|
|
268
274
|
kind: parseKindFilter(takeOption(args, '--kind')),
|
|
269
275
|
status: parseStatusFilter(takeOption(args, '--status')),
|
|
270
276
|
...parseCallerFilter(args),
|
|
277
|
+
...(args.includes('--include-archives') ? { includeArchives: true } : {}),
|
|
271
278
|
};
|
|
272
279
|
}
|
|
273
280
|
/** One header line stating the caller filter, shown only when one is set. */
|
|
@@ -322,6 +329,23 @@ async function decisionStats(ctx) {
|
|
|
322
329
|
]);
|
|
323
330
|
return { exitCode: 0, message: [...header, ...lines].join('\n'), rawOutput: true };
|
|
324
331
|
}
|
|
332
|
+
async function decisionRotate(ctx) {
|
|
333
|
+
const args = ctx.args.slice(1);
|
|
334
|
+
assertLedgerArgs(args, ['--keep-last', '--to'], ['--all', '--json']);
|
|
335
|
+
const keepLast = args.includes('--keep-last') ? takeRequiredOption(args, '--keep-last') : undefined;
|
|
336
|
+
const all = args.includes('--all');
|
|
337
|
+
try {
|
|
338
|
+
parseDecisionLedgerRetention(keepLast, all);
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
throw new DecisionAskUsageError('invalid-retention', error instanceof Error ? error.message : String(error));
|
|
342
|
+
}
|
|
343
|
+
const result = await rotateDecisionLedger({ projectRoot: ctx.cwd, keepLast, all,
|
|
344
|
+
to: args.includes('--to') ? takeRequiredOption(args, '--to') : undefined });
|
|
345
|
+
if (args.includes('--json'))
|
|
346
|
+
return jsonResult(result);
|
|
347
|
+
return { exitCode: 0, rawOutput: true, message: `Archived ${result.archived} line(s)${result.archivePath ? ` to ${result.archivePath}` : ''}; retained ${result.retained} line(s) inline (${result.malformed} malformed).` };
|
|
348
|
+
}
|
|
325
349
|
async function executeDecision(ctx) {
|
|
326
350
|
const [command, subcommand, ...rest] = ctx.args;
|
|
327
351
|
const options = { cwd: ctx.cwd, frameworkRoot: ctx.frameworkRoot, env: process.env };
|
|
@@ -334,6 +358,8 @@ async function executeDecision(ctx) {
|
|
|
334
358
|
return await decisionAsk(ctx);
|
|
335
359
|
if (command === 'history')
|
|
336
360
|
return await decisionHistory(ctx);
|
|
361
|
+
if (command === 'rotate')
|
|
362
|
+
return await decisionRotate(ctx);
|
|
337
363
|
if (command === 'pricing') {
|
|
338
364
|
const args = ctx.args.slice(1);
|
|
339
365
|
assertLedgerArgs(args, ['--model'], ['--refresh', '--dry-run', '--offline', '--allow-snapshot', '--json']);
|
|
@@ -19,10 +19,8 @@
|
|
|
19
19
|
* @see docs/contracts/effect-ledger.v1.md
|
|
20
20
|
*/
|
|
21
21
|
import { readFile } from 'node:fs/promises';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { readAiwgConfig } from '../../config/aiwg-config.js';
|
|
25
|
-
import { CORE_EFFECT_KINDS, DEFAULT_LEDGER_KEY_SERVICE, EFFECT_EXIT_CODES, EFFECT_SUBSYSTEMS, EffectLedgerError, LEDGER_TEST_KEY_ENV, LedgerSigningKey, activeKey, containsRestrictedMaterial, createBuiltinVerifierRegistry, createTrackerVerifiers, credentialStoreKeyProvider, effectId as deriveEffectId, effectOutputJson, environmentTestKeyProvider, generateLedgerKeySecret, initLedgerKeyring, inspectLedgerLocks, isValidEffectId, ledgerLockRecoveryVerifier, lookupEffect, openEffectLedger, payloadDigest, readLedgerKeyring, reconcileEffect, recordIntent, recordOutcome, recordReconciled, recoverStaleLedgerLock, rotateKey, runVerifier, staticKeyProvider, verifyDecisionLinks, verifyLedger, writeCheckpoint, } from '../../effects/index.js';
|
|
22
|
+
import { resolve } from 'node:path';
|
|
23
|
+
import { CORE_EFFECT_KINDS, EFFECT_EXIT_CODES, EFFECT_SUBSYSTEMS, EffectLedgerError, LedgerSigningKey, activeKey, containsRestrictedMaterial, createBuiltinVerifierRegistry, createTrackerVerifiers, effectId as deriveEffectId, effectOutputJson, generateLedgerKeySecret, initLedgerKeyring, inspectLedgerLocks, isValidEffectId, ledgerLockRecoveryVerifier, lookupEffect, payloadDigest, readLedgerKeyring, reconcileEffect, recordIntent, recordOutcome, recordReconciled, recoverStaleLedgerLock, rotateKey, runVerifier, staticKeyProvider, verifyDecisionLinks, verifyLedger, writeCheckpoint, } from '../../effects/index.js';
|
|
26
24
|
import { parseLedgerPrivateKey } from '../../effects/keys.js';
|
|
27
25
|
import { JsonlOperatorDecisionStore, verifyDecisionChain } from '../../audit/operator-decision.js';
|
|
28
26
|
import { readConfig, readGitRemoteUrls } from '../../tracker/project-inputs.js';
|
|
@@ -166,77 +164,7 @@ function keyValue(entry, flag) {
|
|
|
166
164
|
throw usage(`--${flag} expects key=value`, 'invalid-key-value');
|
|
167
165
|
return [entry.slice(0, eq), entry.slice(eq + 1)];
|
|
168
166
|
}
|
|
169
|
-
|
|
170
|
-
function defaultProject(projectDir) {
|
|
171
|
-
const cleaned = basename(resolve(projectDir)).replace(/[^A-Za-z0-9._:/@-]/g, '-').replace(/^[^A-Za-z0-9]+/, '');
|
|
172
|
-
return cleaned && IDENTIFIER.test(cleaned) ? cleaned : 'project';
|
|
173
|
-
}
|
|
174
|
-
async function hostConfig(projectDir) {
|
|
175
|
-
let config;
|
|
176
|
-
try {
|
|
177
|
-
config = await readAiwgConfig(projectDir);
|
|
178
|
-
}
|
|
179
|
-
catch {
|
|
180
|
-
throw new EffectLedgerError('usage', 'aiwg.config could not be read', 'config-unreadable');
|
|
181
|
-
}
|
|
182
|
-
const effects = config?.effects;
|
|
183
|
-
if (effects === undefined || effects === null)
|
|
184
|
-
return {};
|
|
185
|
-
if (typeof effects !== 'object' || Array.isArray(effects))
|
|
186
|
-
throw usage('aiwg.config effects must be an object', 'config-invalid');
|
|
187
|
-
return effects;
|
|
188
|
-
}
|
|
189
|
-
function testContext(env) {
|
|
190
|
-
return env.VITEST === 'true' || env.NODE_ENV === 'test' || env.CI === 'true' || env.CI === '1';
|
|
191
|
-
}
|
|
192
|
-
function keyStore(session, role) {
|
|
193
|
-
const { deps, config, account } = session;
|
|
194
|
-
if (deps.keyStore)
|
|
195
|
-
return deps.keyStore(role, account);
|
|
196
|
-
if (session.env[LEDGER_TEST_KEY_ENV] && testContext(session.env))
|
|
197
|
-
return null;
|
|
198
|
-
const provider = config.keyProvider ?? {};
|
|
199
|
-
try {
|
|
200
|
-
if (provider.type === 'file') {
|
|
201
|
-
if (!provider.path)
|
|
202
|
-
throw new Error('missing path');
|
|
203
|
-
const pathname = resolve(session.projectDir, provider.path);
|
|
204
|
-
return createSecretStore({ useFile: true, allowFile: true, pathname: role === 'next' ? `${pathname}.next` : pathname, service: DEFAULT_LEDGER_KEY_SERVICE, account });
|
|
205
|
-
}
|
|
206
|
-
if (provider.type !== undefined && provider.type !== 'credential-store')
|
|
207
|
-
throw new Error('unknown provider');
|
|
208
|
-
return createSecretStore({ service: provider.service ?? DEFAULT_LEDGER_KEY_SERVICE, account: role === 'next' ? `${account}.next` : account });
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
|
-
throw new EffectLedgerError('key-unavailable', 'Effect ledger key provider is not configured correctly on this host', 'key-provider-invalid');
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
function keyProvider(session) {
|
|
215
|
-
const store = keyStore(session, 'current');
|
|
216
|
-
if (!store)
|
|
217
|
-
return environmentTestKeyProvider(session.env);
|
|
218
|
-
return credentialStoreKeyProvider({ account: session.account, store });
|
|
219
|
-
}
|
|
220
|
-
function describeKeyProvider(session) {
|
|
221
|
-
if (session.deps.keyStore)
|
|
222
|
-
return { type: 'injected', account: session.account };
|
|
223
|
-
if (session.env[LEDGER_TEST_KEY_ENV] && testContext(session.env))
|
|
224
|
-
return { type: 'environment-test-key' };
|
|
225
|
-
const provider = session.config.keyProvider ?? {};
|
|
226
|
-
if (provider.type === 'file')
|
|
227
|
-
return { type: 'file', account: session.account };
|
|
228
|
-
return { type: 'credential-store', service: provider.service ?? DEFAULT_LEDGER_KEY_SERVICE, account: session.account };
|
|
229
|
-
}
|
|
230
|
-
function openLedger(session, registry) {
|
|
231
|
-
const writer = session.config.writer ?? 'cli';
|
|
232
|
-
return openEffectLedger({
|
|
233
|
-
projectDir: session.projectDir, scope: session.scope, writer, keyProvider: keyProvider(session),
|
|
234
|
-
...(session.deps.clock ? { clock: session.deps.clock } : {}),
|
|
235
|
-
...(session.deps.sink ? { sink: session.deps.sink } : {}),
|
|
236
|
-
...(registry ? { verifiers: registry } : {}),
|
|
237
|
-
...(session.deps.lockTimeoutMs ? { lockTimeoutMs: session.deps.lockTimeoutMs } : {}),
|
|
238
|
-
});
|
|
239
|
-
}
|
|
167
|
+
import { effectSession, keyStore, describeKeyProvider, openLedger } from '../../effects/host.js';
|
|
240
168
|
/**
|
|
241
169
|
* The one place the CLI builds its verifier registry: the built-ins, lock
|
|
242
170
|
* recovery, the tracker verifiers (#2719) and any injected extensions.
|
|
@@ -804,15 +732,7 @@ async function execute(ctx, deps) {
|
|
|
804
732
|
throw usage('--format must be json or text', 'invalid-format');
|
|
805
733
|
const projectDir = resolve(one(parsed, 'project-dir') ?? ctx.cwd ?? process.cwd());
|
|
806
734
|
const subsystem = resolveSubsystem(parsed);
|
|
807
|
-
const
|
|
808
|
-
const tenant = config.tenant ?? 'local';
|
|
809
|
-
const project = config.project ?? defaultProject(projectDir);
|
|
810
|
-
if (!IDENTIFIER.test(tenant) || !IDENTIFIER.test(project))
|
|
811
|
-
throw usage('aiwg.config effects tenant and project must be identifiers', 'config-invalid');
|
|
812
|
-
const scope = { tenant, project, subsystem };
|
|
813
|
-
const env = deps.env ?? process.env;
|
|
814
|
-
const account = config.keyProvider?.account ?? `ledger/${tenant}/${project}/${subsystem}`;
|
|
815
|
-
const session = { projectDir, scope, config, account, deps, env };
|
|
735
|
+
const session = await effectSession(projectDir, subsystem, deps);
|
|
816
736
|
let rendered;
|
|
817
737
|
switch (sub) {
|
|
818
738
|
case 'id':
|
|
@@ -56,7 +56,7 @@ export function parseGuidedSetupFlags(args, command) {
|
|
|
56
56
|
const separator = raw.indexOf('=');
|
|
57
57
|
const provider = raw.slice(0, separator) === 'jev' ? 'jev' : normalizeProviderDefinitionId(raw.slice(0, separator));
|
|
58
58
|
if (separator < 1 || !provider || provider === 'generic')
|
|
59
|
-
throw new Error('--credential requires a known <provider>=
|
|
59
|
+
throw new Error('--credential requires a known <provider>=stdin|file:PATH|store:NAME|env:NAME.');
|
|
60
60
|
credentials[provider] = credentialReference(raw.slice(separator + 1));
|
|
61
61
|
}
|
|
62
62
|
if (tokenStdin) {
|
|
@@ -191,7 +191,7 @@ export const initHandler = {
|
|
|
191
191
|
--intent code|docs|ops|mixed Workspace usage intent
|
|
192
192
|
--jev enable|decline|defer Optional external decision service (default off)
|
|
193
193
|
--region <r> --endpoint <url> --verify --remove --token-stdin Existing Jev options
|
|
194
|
-
--credential <provider>=
|
|
194
|
+
--credential <provider>=stdin|file:PATH|store:NAME|env:NAME Credential reference only
|
|
195
195
|
--providers <list> Provider IDs
|
|
196
196
|
--default-scripts true|false Include default scripts
|
|
197
197
|
--custom-scripts true|false Include custom scripts
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* @implements #804
|
|
19
19
|
*/
|
|
20
20
|
import path from 'path';
|
|
21
|
+
import { sensitiveInputOptions } from '../../auth/sensitive-input.js';
|
|
21
22
|
import { installPackage } from '../../packages/registry.js';
|
|
22
23
|
import { recordDeployment } from '../../packages/package-registry.js';
|
|
23
24
|
import { marketplaceConfigDir, resolveVerificationPolicy } from '../../marketplace/exchange.js';
|
|
@@ -59,6 +60,7 @@ export const installHandler = {
|
|
|
59
60
|
' --deploy Deploy immediately after install',
|
|
60
61
|
' --provider <name> Target provider (claude, copilot, cursor...)',
|
|
61
62
|
' --target <dir> Project directory to deploy into',
|
|
63
|
+
' --token-interactive | --token-stdin | --token-file <path> | --token-store <name> | --token-env <name>',
|
|
62
64
|
' --refresh Force re-pull even if cached',
|
|
63
65
|
' --ref <tag-or-sha> Resolve this ref and lock its immutable commit',
|
|
64
66
|
' --package <id> Select one wrapper when a repository contains several',
|
|
@@ -69,6 +71,9 @@ export const installHandler = {
|
|
|
69
71
|
].join('\n'),
|
|
70
72
|
};
|
|
71
73
|
}
|
|
74
|
+
if (/^https?:\/\/[^/]*@/.test(rawRef)) {
|
|
75
|
+
return { exitCode: 1, message: 'Credentials in Git URLs are refused; use --token-stdin, --token-file, --token-store or --token-env.' };
|
|
76
|
+
}
|
|
72
77
|
const deploy = hasFlag(ctx.args, '--deploy');
|
|
73
78
|
const refresh = hasFlag(ctx.args, '--refresh');
|
|
74
79
|
const provider = parseFlag(ctx.args, '--provider') ?? 'claude';
|
|
@@ -97,6 +102,7 @@ export const installHandler = {
|
|
|
97
102
|
const resolvedPolicy = await resolveVerificationPolicy(policyName, scope);
|
|
98
103
|
const installed = await installPackage(rawRef, {
|
|
99
104
|
refresh,
|
|
105
|
+
tokenInput: sensitiveInputOptions(ctx.args, 'token'),
|
|
100
106
|
ref: parseFlag(ctx.args, '--ref'),
|
|
101
107
|
packageSelector: parseFlag(ctx.args, '--package'),
|
|
102
108
|
verify,
|
|
@@ -21,9 +21,22 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
|
21
21
|
import { createHash, randomUUID } from 'crypto';
|
|
22
22
|
import { pathToFileURL } from 'node:url';
|
|
23
23
|
import { projectAiwgPath } from '../../config/project-artifacts.js';
|
|
24
|
+
import { resolveSensitiveInput, sensitiveInputOptions } from '../../auth/sensitive-input.js';
|
|
24
25
|
const DEFAULT_PORT = 8200;
|
|
25
26
|
const DEFAULT_BIND = '127.0.0.1';
|
|
26
27
|
const DEFAULT_AIWG_SERVE = 'http://127.0.0.1:7337';
|
|
28
|
+
/** Optional input: no source keeps registration/cache behavior, even at a terminal. */
|
|
29
|
+
export async function resolveLocalExecutorToken(args, dependencies = {}) {
|
|
30
|
+
const source = sensitiveInputOptions(args, 'token');
|
|
31
|
+
const environment = dependencies.environment ?? process.env;
|
|
32
|
+
const explicit = source.interactive || source.stdin || source.file !== undefined || source.store !== undefined || source.env !== undefined;
|
|
33
|
+
if (!explicit) {
|
|
34
|
+
if (!environment.AIWG_EXECUTOR_TOKEN)
|
|
35
|
+
return undefined;
|
|
36
|
+
source.env = 'AIWG_EXECUTOR_TOKEN';
|
|
37
|
+
}
|
|
38
|
+
return resolveSensitiveInput(source, { ...dependencies, environment });
|
|
39
|
+
}
|
|
27
40
|
/**
|
|
28
41
|
* Resolve or generate-once the executor identity.
|
|
29
42
|
* Written to `.aiwg/local-executor/identity.json` so restarts reuse the same
|
|
@@ -122,6 +135,14 @@ export const localExecutorServeHandler = {
|
|
|
122
135
|
// The subcommand is: `aiwg local-executor serve [flags]`
|
|
123
136
|
// ctx.args[0] === 'serve' (consumed by router) or flags directly
|
|
124
137
|
const flagArgs = ctx.args[0] === 'serve' ? ctx.args.slice(1) : ctx.args;
|
|
138
|
+
let preexistingToken;
|
|
139
|
+
try {
|
|
140
|
+
preexistingToken = await resolveLocalExecutorToken(flagArgs);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
return { exitCode: 1, message: error.message };
|
|
144
|
+
}
|
|
145
|
+
const redact = (message) => preexistingToken ? message.split(preexistingToken).join('[REDACTED]') : message;
|
|
125
146
|
const opts = parseLocalExecutorArgs(flagArgs);
|
|
126
147
|
warnIfNonLoopback(opts.bind);
|
|
127
148
|
// ── Dynamic imports (same pattern as serve.ts) ──────────────────────────
|
|
@@ -138,7 +159,7 @@ export const localExecutorServeHandler = {
|
|
|
138
159
|
}
|
|
139
160
|
catch (err) {
|
|
140
161
|
return {
|
|
141
|
-
message: `Failed to load executor-shim: ${err.message}`,
|
|
162
|
+
message: `Failed to load executor-shim: ${redact(err.message)}`,
|
|
142
163
|
exitCode: 1,
|
|
143
164
|
};
|
|
144
165
|
}
|
|
@@ -148,7 +169,7 @@ export const localExecutorServeHandler = {
|
|
|
148
169
|
}
|
|
149
170
|
catch (err) {
|
|
150
171
|
return {
|
|
151
|
-
message: `Failed to load DaemonSupervisor: ${err.message}`,
|
|
172
|
+
message: `Failed to load DaemonSupervisor: ${redact(err.message)}`,
|
|
152
173
|
exitCode: 1,
|
|
153
174
|
};
|
|
154
175
|
}
|
|
@@ -208,7 +229,7 @@ export const localExecutorServeHandler = {
|
|
|
208
229
|
restBase,
|
|
209
230
|
wsBase,
|
|
210
231
|
aiwgServeUrl: opts.aiwgServe,
|
|
211
|
-
token:
|
|
232
|
+
token: preexistingToken,
|
|
212
233
|
tokenPath: projectAiwgPath(process.cwd(), 'local-executor', 'tokens', `${createHash('sha256').update(`${opts.aiwgServe}\0${opts.executorId}`).digest('hex')}.json`),
|
|
213
234
|
});
|
|
214
235
|
// Register with aiwg-serve
|
|
@@ -218,7 +239,7 @@ export const localExecutorServeHandler = {
|
|
|
218
239
|
}
|
|
219
240
|
catch (err) {
|
|
220
241
|
return {
|
|
221
|
-
message: `Failed to register with aiwg-serve at ${opts.aiwgServe}: ${err.message}\n` +
|
|
242
|
+
message: `Failed to register with aiwg-serve at ${opts.aiwgServe}: ${redact(err.message)}\n` +
|
|
222
243
|
'Is aiwg serve running? Start it with: aiwg serve --no-open',
|
|
223
244
|
exitCode: 1,
|
|
224
245
|
};
|
|
@@ -235,7 +256,7 @@ export const localExecutorServeHandler = {
|
|
|
235
256
|
catch (err) {
|
|
236
257
|
await shim.deregister();
|
|
237
258
|
return {
|
|
238
|
-
message: `Failed to start executor server: ${err.message}`,
|
|
259
|
+
message: `Failed to start executor server: ${redact(err.message).split(token).join('[REDACTED]')}`,
|
|
239
260
|
exitCode: 1,
|
|
240
261
|
};
|
|
241
262
|
}
|
|
@@ -304,9 +325,15 @@ export const localExecutorHandler = {
|
|
|
304
325
|
' --aiwg-serve <url> Registration target (default: http://127.0.0.1:7337)\n' +
|
|
305
326
|
' --max-concurrency <n> Max concurrent missions (default: 4)\n' +
|
|
306
327
|
' --executor-id <uuid> Override stable executor ID\n' +
|
|
328
|
+
' --token-interactive Read optional existing bearer without echo\n' +
|
|
329
|
+
' --token-stdin Read optional existing bearer from stdin\n' +
|
|
330
|
+
' --token-file <path> Read optional existing bearer from a private file\n' +
|
|
331
|
+
' --token-store <name> Read optional existing bearer from the OS store\n' +
|
|
332
|
+
' --token-env <NAME> Read optional existing bearer from a named variable\n' +
|
|
307
333
|
'\n' +
|
|
308
334
|
'Environment:\n' +
|
|
309
|
-
' AIWG_SERVE_ENDPOINT Override --aiwg-serve default\n'
|
|
335
|
+
' AIWG_SERVE_ENDPOINT Override --aiwg-serve default\n' +
|
|
336
|
+
' AIWG_EXECUTOR_TOKEN Optional preexisting executor bearer\n');
|
|
310
337
|
return { exitCode: 0 };
|
|
311
338
|
}
|
|
312
339
|
return {
|