@aiwg/cli 2026.10.6 → 2026.10.7

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.
Files changed (84) hide show
  1. package/bin/aiwg.mjs +4 -2
  2. package/dist/src/a2a/agent-card.js +11 -0
  3. package/dist/src/api/index.d.ts +1 -0
  4. package/dist/src/api/index.js +1 -0
  5. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
  6. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
  7. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
  8. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
  9. package/dist/src/artifacts/fortemi-shard-export.js +284 -0
  10. package/dist/src/artifacts/index-preparation.js +2 -0
  11. package/dist/src/auth/client.js +5 -1
  12. package/dist/src/auth/credential-store.js +5 -1
  13. package/dist/src/auth/sensitive-input.js +151 -0
  14. package/dist/src/channel/manager.mjs +3 -0
  15. package/dist/src/cli/handlers/agentcard.js +47 -25
  16. package/dist/src/cli/handlers/auth.js +22 -2
  17. package/dist/src/cli/handlers/decision.js +3 -2
  18. package/dist/src/cli/handlers/effect.js +4 -84
  19. package/dist/src/cli/handlers/guided-setup.js +1 -1
  20. package/dist/src/cli/handlers/init.js +1 -1
  21. package/dist/src/cli/handlers/install.js +6 -0
  22. package/dist/src/cli/handlers/marketplace.js +15 -4
  23. package/dist/src/cli/handlers/setup.js +24 -20
  24. package/dist/src/cli/handlers/use.js +57 -15
  25. package/dist/src/cli/prompt-utils.js +73 -0
  26. package/dist/src/config/gitignore.js +4 -0
  27. package/dist/src/config/workspace.js +2 -1
  28. package/dist/src/decision/adapters/jev.js +20 -31
  29. package/dist/src/decision/ask.js +6 -1
  30. package/dist/src/decision/benchmark.js +19 -6
  31. package/dist/src/decision/qualification/gate-evidence.js +6 -4
  32. package/dist/src/decision/qualification/load-staged.js +47 -10
  33. package/dist/src/decision/qualification/runner.js +7 -3
  34. package/dist/src/discovery/interview.js +10 -5
  35. package/dist/src/discovery/plan-apply.js +26 -30
  36. package/dist/src/discovery/plan-validation.js +2 -2
  37. package/dist/src/effects/host.js +91 -0
  38. package/dist/src/extensions/commands/definitions.js +2 -2
  39. package/dist/src/extensions/project-local-gitignore.js +2 -0
  40. package/dist/src/issues/cli.js +16 -5
  41. package/dist/src/issues/live.js +44 -12
  42. package/dist/src/marketplace/exchange.js +18 -6
  43. package/dist/src/marketplace/provenance.js +3 -2
  44. package/dist/src/mcp/adapters/codex-runtime.js +46 -76
  45. package/dist/src/mcp/cli.mjs +17 -90
  46. package/dist/src/mcp/grok-build-config.mjs +2 -1
  47. package/dist/src/mcp/muse-native-config.mjs +2 -1
  48. package/dist/src/mcp/tools/worker-invoke.mjs +13 -2
  49. package/dist/src/memory/project-registry.js +2 -1
  50. package/dist/src/mission-protocol/worker-status.js +91 -1
  51. package/dist/src/models/worker-installation.d.mts +1 -1
  52. package/dist/src/models/worker-installation.mjs +65 -4
  53. package/dist/src/packages/adapters/git.js +99 -23
  54. package/dist/src/packages/adapters/gitea.js +8 -27
  55. package/dist/src/packages/adapters/github.js +7 -25
  56. package/dist/src/packages/registry.js +4 -4
  57. package/dist/src/process/cli-command.d.mts +5 -0
  58. package/dist/src/process/cli-command.mjs +42 -0
  59. package/dist/src/serve/worker-invocation.js +10 -0
  60. package/dist/src/smiths/context-pipeline/claude-hook.js +9 -9
  61. package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
  62. package/dist/src/smiths/context-pipeline/finalization.js +3 -2
  63. package/dist/src/smiths/context-pipeline/generator.js +7 -19
  64. package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
  65. package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
  66. package/dist/src/smiths/context-pipeline/workspace-context.js +4 -12
  67. package/dist/src/tracker/project-inputs.js +2 -0
  68. package/dist/src/update/notifier.mjs +2 -4
  69. package/package.json +1 -1
  70. package/tools/agents/providers/base.mjs +5 -1
  71. package/tools/agents/providers/claude.mjs +5 -2
  72. package/tools/agents/providers/codex.mjs +4 -1
  73. package/tools/agents/providers/context-write.mjs +28 -0
  74. package/tools/agents/providers/copilot.mjs +5 -2
  75. package/tools/agents/providers/hermes.mjs +5 -2
  76. package/tools/agents/providers/omp.mjs +3 -1
  77. package/tools/agents/providers/rule-delivery.mjs +29 -12
  78. package/tools/agents/providers/warp.mjs +4 -1
  79. package/tools/agents/providers/windsurf.mjs +6 -3
  80. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
  81. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
  82. package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
  83. package/tools/ralph-external/process-monitor.mjs +42 -4
  84. 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 (parsed.bearer !== undefined)
80
- opts.bearer = parsed.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
- console.log(JSON.stringify({
108
+ log(JSON.stringify({
92
109
  ok: false,
93
110
  host: parsed.host,
94
111
  instance: parsed.instance,
95
112
  error: message,
96
- }, null, 2));
113
+ }, jsonReplacer, 2));
97
114
  }
98
115
  else {
99
- console.error(`AgentCard verification FAILED for ${parsed.host}/${parsed.instance}`);
100
- console.error(` ${message}`);
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
- console.log(JSON.stringify({
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
- }, null, 2));
138
+ }, jsonReplacer, 2));
122
139
  }
123
140
  else {
124
141
  const banner = parsed.skipVerify ? '(verification SKIPPED)' : 'VERIFIED';
125
- console.log(`AgentCard ${banner} for ${parsed.host}/${parsed.instance}`);
126
- console.log(` name: ${verified.card.name}`);
127
- console.log(` protocolVersion: ${verified.card.protocolVersion}`);
128
- console.log(` url: ${verified.card.url}`);
129
- console.log(` version: ${verified.card.version}`);
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
- console.log(` preferredTransport: ${verified.card.preferredTransport}`);
148
+ log(` preferredTransport: ${verified.card.preferredTransport}`);
132
149
  }
133
150
  if (verified.kid) {
134
- console.log(` signing kid: ${verified.kid}`);
151
+ log(` signing kid: ${verified.kid}`);
135
152
  }
136
- console.log(` verifiedAt: ${verified.verifiedAt}`);
153
+ log(` verifiedAt: ${verified.verifiedAt}`);
137
154
  const exts = verified.card.capabilities?.extensions ?? [];
138
155
  if (exts.length > 0) {
139
- console.log(` extensions:`);
156
+ log(` extensions:`);
140
157
  for (const e of exts) {
141
158
  const tag = e.required ? '[required]' : '[optional]';
142
- console.log(` ${tag} ${e.uri}`);
159
+ log(` ${tag} ${e.uri}`);
143
160
  }
144
161
  }
145
162
  const skills = verified.card.skills ?? [];
146
163
  if (skills.length > 0) {
147
- console.log(` skills (${skills.length}):`);
164
+ log(` skills (${skills.length}):`);
148
165
  for (const s of skills.slice(0, 5)) {
149
- console.log(` - ${s.id}${s.name ? ' (' + s.name + ')' : ''}`);
166
+ log(` - ${s.id}${s.name ? ' (' + s.name + ')' : ''}`);
150
167
  }
151
168
  if (skills.length > 5)
152
- console.log(` ... and ${skills.length - 5} more`);
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 <token> Optional bearer token for the card endpoint');
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 : String(error) };
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,3 +1,4 @@
1
+ import { resolveSensitiveInput, sensitiveInputOptions } from '../../auth/sensitive-input.js';
1
2
  import { decisionPricing } from '../../decision/pricing.js';
2
3
  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
4
  function jsonResult(value, exitCode = 0) {
@@ -17,7 +18,7 @@ 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
23
  ' history [--since <date>] [--provider <id>] [--session <id>] [--kind yes-no|choices|scale] [--status answered|fallback] [--caller <tag>|<prefix>* | --no-caller] [--json]',
23
24
  ' pricing [--refresh --model jev-X.Y.Z] [--dry-run] [--offline | --allow-snapshot] [--json]',
@@ -101,7 +102,7 @@ function takeAttributionOption(args, name, reason) {
101
102
  return value;
102
103
  }
103
104
  async function decisionSetupJev(rest) {
104
- const token = rest.includes('--token-stdin') ? (await readStdin()).trim() : undefined;
105
+ const token = rest.includes('--remove') ? undefined : await resolveSensitiveInput(sensitiveInputOptions(rest, 'token'));
105
106
  const result = await setupJev({
106
107
  token,
107
108
  region: takeOption(rest, '--region'),
@@ -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 { basename, resolve } from 'node:path';
23
- import { createSecretStore } from '../../auth/credential-store.js';
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
- const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:/@-]{0,255}$/;
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 config = await hostConfig(projectDir);
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>=env:NAME|file:PATH|stdin.');
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>=env:NAME|file:PATH|stdin Credential reference only
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,
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import fs from 'node:fs';
11
11
  import path from 'node:path';
12
+ import { sensitiveInputOptions } from '../../auth/sensitive-input.js';
12
13
  import { searchSkills } from '../../skills/registry.js';
13
14
  import { installPackage, listInstalledPackages } from '../../packages/registry.js';
14
15
  import { GitAdapter } from '../../packages/adapters/git.js';
@@ -24,7 +25,7 @@ const USAGE = [
24
25
  ' aiwg marketplace verify <package|lock> Verify cached content and receipts offline',
25
26
  ' aiwg marketplace export <package> --output <archive.json>',
26
27
  ' aiwg marketplace import <archive.json> [--project-local]',
27
- ' aiwg marketplace publish <source> --key <pem> --publisher <id>',
28
+ ' aiwg marketplace publish <source> --publisher <id> [--key-file <pem> | --key-stdin]',
28
29
  ' aiwg marketplace remove <catalog-id>',
29
30
  ' aiwg marketplace list List installed marketplace packages',
30
31
  '',
@@ -33,6 +34,11 @@ const USAGE = [
33
34
  ' --project-local Store/read state below the target project .aiwg directory',
34
35
  ' --global Store/read state in the user AIWG directory (default)',
35
36
  ' --policy <name> Named trust policy or JSON policy path',
37
+ 'Git fetch token sources:',
38
+ ' --token-interactive | --token-stdin | --token-file <path> | --token-store <name> | --token-env <name>',
39
+ 'Publish key sources (multiline PEM):',
40
+ ' --key-interactive | --key-stdin | --key-file <path> | --key-store <name> | --key-env <name>',
41
+ ' --key <path> Compatibility alias for --key-file (private file required)',
36
42
  ].join('\n');
37
43
  function parseFlag(args, flag) {
38
44
  const idx = args.indexOf(flag);
@@ -105,7 +111,10 @@ async function handleAdd(ctx) {
105
111
  const requestedRef = parseFlag(ctx.args, '--ref') ?? 'HEAD';
106
112
  const adapter = new GitAdapter();
107
113
  const source = { gitUrl: argument, ref: requestedRef, label: argument };
108
- const cachePath = await adapter.fetch(source, { refresh: hasFlag(ctx.args, '--refresh') });
114
+ const cachePath = await adapter.fetch(source, {
115
+ refresh: hasFlag(ctx.args, '--refresh'),
116
+ tokenInput: sensitiveInputOptions(ctx.args, 'token'),
117
+ });
109
118
  const identity = await inspectGitCheckout(cachePath);
110
119
  const candidates = [
111
120
  path.join(cachePath, 'aiwg-marketplace-catalog.json'),
@@ -251,6 +260,7 @@ async function handleInstall(ctx) {
251
260
  }
252
261
  const installed = await installPackage(rawRef, {
253
262
  configDir,
263
+ tokenInput: sensitiveInputOptions(ctx.args, 'token'),
254
264
  ref,
255
265
  packageSelector: parseFlag(ctx.args, '--package'),
256
266
  refresh: hasFlag(ctx.args, '--refresh'),
@@ -342,8 +352,8 @@ async function handlePublish(ctx) {
342
352
  return argument;
343
353
  const privateKeyPath = parseFlag(ctx.args, '--key');
344
354
  const publisher = parseFlag(ctx.args, '--publisher');
345
- if (!privateKeyPath || !publisher) {
346
- return { exitCode: 1, message: 'Error: Usage: aiwg marketplace publish <source> --key <ed25519.pem> --publisher <id> [--output <dir>]' };
355
+ if (!publisher) {
356
+ return { exitCode: 1, message: 'Error: Usage: aiwg marketplace publish <source> [--key-file <ed25519.pem> | --key-stdin] --publisher <id> [--output <dir>]' };
347
357
  }
348
358
  try {
349
359
  const outputDir = parseFlag(ctx.args, '--output') ?? path.join(path.resolve(argument), '.aiwg', 'marketplace');
@@ -355,6 +365,7 @@ async function handlePublish(ctx) {
355
365
  sourceDir: argument,
356
366
  outputDir,
357
367
  privateKeyPath,
368
+ keyInput: sensitiveInputOptions(ctx.args, 'key'),
358
369
  publicKeyPath: parseFlag(ctx.args, '--public-key'),
359
370
  keyId: parseFlag(ctx.args, '--key-id'),
360
371
  publisher,
@@ -1,4 +1,7 @@
1
1
  import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
2
+ import { helperEnvironment, HELPER_ENV_ALLOWLIST } from '../../process/cli-command.mjs';
3
+ import { readPrivateValueFile } from '../../auth/sensitive-input.js';
4
+ import { setupJev } from '../../decision/driver.js';
2
5
  import { existsSync } from 'fs';
3
6
  import { emptyConfig, FORCE_PUSH_POLICY_ALIAS_NOTE, normalizeForcePushPolicy as normalizeForcePushPolicyShared, getConfigPath, getProjectDir, readAiwgConfig, resolveRemoteProvider, VALID_PROVIDERS, writeAiwgConfig, } from '../../config/aiwg-config.js';
4
7
  import { AiwgError, EXIT_CODES } from '../errors.js';
@@ -133,6 +136,7 @@ export function parseSetupProjectOptions(ctx) {
133
136
  }
134
137
  export function detectGitRemotes(projectDir) {
135
138
  const result = boundedSpawnSync('git', ['-C', projectDir, 'remote', '-v'], 'quick', {
139
+ env: helperEnvironment(),
136
140
  encoding: 'utf-8',
137
141
  stdio: ['ignore', 'pipe', 'ignore'],
138
142
  });
@@ -156,6 +160,7 @@ export function detectGitRemotes(projectDir) {
156
160
  }
157
161
  function detectDefaultBranch(projectDir) {
158
162
  const symbolic = boundedSpawnSync('git', ['-C', projectDir, 'symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'], 'quick', {
163
+ env: helperEnvironment(),
159
164
  encoding: 'utf-8',
160
165
  stdio: ['ignore', 'pipe', 'ignore'],
161
166
  });
@@ -163,6 +168,7 @@ function detectDefaultBranch(projectDir) {
163
168
  if (symbolic.status === 0 && fromOrigin)
164
169
  return fromOrigin;
165
170
  const branch = boundedSpawnSync('git', ['-C', projectDir, 'branch', '--show-current'], 'quick', {
171
+ env: helperEnvironment(),
166
172
  encoding: 'utf-8',
167
173
  stdio: ['ignore', 'pipe', 'ignore'],
168
174
  });
@@ -170,6 +176,7 @@ function detectDefaultBranch(projectDir) {
170
176
  }
171
177
  function gitConfig(projectDir, key) {
172
178
  const result = boundedSpawnSync('git', ['-C', projectDir, 'config', '--get', key], 'quick', {
179
+ env: helperEnvironment(),
173
180
  encoding: 'utf-8',
174
181
  stdio: ['ignore', 'pipe', 'ignore'],
175
182
  });
@@ -656,7 +663,7 @@ function defaultGuidedApplyIO(plan, fresh, ctx, projectDir, frameworkRoot, canPr
656
663
  chunks.push(Buffer.from(chunk));
657
664
  return Buffer.concat(chunks).toString('utf8');
658
665
  } } : {}),
659
- readCredentialFile: async (path) => readFile(path, 'utf8'),
666
+ readCredentialFile: async (path) => readPrivateValueFile(path, '--credential'),
660
667
  // stdin-at-apply prompts for the value without echoing it anywhere: the
661
668
  // label names the consumer only, and the value never reaches output.
662
669
  ...(canPrompt ? { promptCredential: async (label) => {
@@ -674,26 +681,20 @@ function defaultGuidedApplyIO(plan, fresh, ctx, projectDir, frameworkRoot, canPr
674
681
  await writeSetupPlan(projectDir, next);
675
682
  },
676
683
  owners: {
677
- configureJev: async ({ action, token, options, credential }) => {
684
+ configureJev: async ({ action, token, options }) => {
678
685
  if (options != null)
679
686
  validateJevOptions(options);
680
687
  const settings = (options ?? {});
681
- const args = ['decision', 'setup', 'jev', ...(settings.remove === true ? ['--remove'] : ['--token-stdin']),
682
- ...(typeof settings.region === 'string' ? ['--region', settings.region] : []),
683
- ...(typeof settings.endpoint === 'string' ? ['--endpoint', settings.endpoint] : []),
684
- ...(settings.verify === true ? ['--verify'] : [])];
685
- const environment = { ...process.env };
686
- delete environment.JEV_API_KEY;
687
- delete environment.AIWG_DECISION_JEV_API_KEY;
688
- if (credential?.source === 'env')
689
- delete environment[credential.name];
690
- const child = boundedSpawnSync(process.execPath, [join(frameworkRoot, 'bin', 'aiwg.mjs'), ...args], 'network', {
691
- cwd: action.workspace, env: environment, input: token ? `${token}\n` : '', encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
692
- maxBuffer: 1024 * 1024,
693
- });
694
- if (child.error || child.status !== 0)
695
- throw new Error('Jev setup failed.');
696
- if (settings.verify === true && settings.remove !== true && JSON.parse(child.stdout).verified !== true) {
688
+ // Invoke the existing owner directly. spawnSync's input option is
689
+ // rendered by NODE_DEBUG=child_process before the child can read it.
690
+ const result = await setupJev({
691
+ token,
692
+ ...(typeof settings.region === 'string' ? { region: settings.region } : {}),
693
+ ...(typeof settings.endpoint === 'string' ? { endpoint: settings.endpoint } : {}),
694
+ verify: settings.verify === true,
695
+ remove: settings.remove === true,
696
+ }, { env: process.env, cwd: action.workspace });
697
+ if (settings.verify === true && settings.remove !== true && result.verified !== true) {
697
698
  throw new Error('Jev verification incomplete.');
698
699
  }
699
700
  },
@@ -711,7 +712,10 @@ function defaultGuidedApplyIO(plan, fresh, ctx, projectDir, frameworkRoot, canPr
711
712
  await writeAiwgConfig(action.workspace, next);
712
713
  const child = boundedSpawnSync(process.execPath, [join(frameworkRoot, 'bin', 'aiwg.mjs'),
713
714
  'doctor', '--probe-workers', '--qualify', '--yes', '--json'], 'long', {
714
- cwd: action.workspace, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 1024 * 1024,
715
+ cwd: action.workspace, env: helperEnvironment(process.env, [...HELPER_ENV_ALLOWLIST,
716
+ 'AIWG_ROOT', 'AIWG_HOME', 'AIWG_CONFIG', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME',
717
+ 'AIWG_ARTIFACTS_PATH', 'AIWG_PROJECT_ARTIFACTS_PATH', 'AIWG_PROJECT_AIWG_DIR',
718
+ 'AIWG_PROJECT_MEMORY_HOME', 'NO_UPDATE_NOTIFIER', 'NO_COLOR', 'CI']), encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 1024 * 1024,
715
719
  });
716
720
  if (child.error || child.status !== 0)
717
721
  throw new Error('Worker qualification failed.');
@@ -813,7 +817,7 @@ Options:
813
817
  --intent code|docs|ops|mixed Workspace usage intent
814
818
  --jev enable|decline|defer Optional external decision service (default off)
815
819
  --region <r> --endpoint <url> --verify --remove --token-stdin Existing Jev options
816
- --credential <provider>=env:NAME|file:PATH|stdin Credential reference only
820
+ --credential <provider>=stdin|file:PATH|store:NAME|env:NAME Credential reference only
817
821
  --yes Confirm writes without prompting
818
822
  --dry-run Preview the setup plan
819
823
  --review Render the saved guided plan per workspace/provider (writes nothing)