@pleri/olam-cli 0.1.196 → 0.1.199
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -0
- package/dist/ask/knowledge-pack.generated.d.ts.map +1 -1
- package/dist/ask/knowledge-pack.generated.js +10 -8
- package/dist/ask/knowledge-pack.generated.js.map +1 -1
- package/dist/commands/auth-list-json.d.ts +34 -0
- package/dist/commands/auth-list-json.d.ts.map +1 -1
- package/dist/commands/auth-list-json.js +24 -0
- package/dist/commands/auth-list-json.js.map +1 -1
- package/dist/commands/auth-migrate.d.ts +212 -0
- package/dist/commands/auth-migrate.d.ts.map +1 -0
- package/dist/commands/auth-migrate.js +465 -0
- package/dist/commands/auth-migrate.js.map +1 -0
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +239 -184
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/bootstrap.d.ts +4 -0
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js +10 -0
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/dispatch.js +11 -1
- package/dist/commands/dispatch.js.map +1 -1
- package/dist/commands/doctor.d.ts +33 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +299 -12
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/kg-mirror.d.ts +18 -2
- package/dist/commands/kg-mirror.d.ts.map +1 -1
- package/dist/commands/kg-mirror.js +78 -3
- package/dist/commands/kg-mirror.js.map +1 -1
- package/dist/commands/mcp/complete.d.ts +36 -0
- package/dist/commands/mcp/complete.d.ts.map +1 -0
- package/dist/commands/mcp/complete.js +66 -0
- package/dist/commands/mcp/complete.js.map +1 -0
- package/dist/commands/mcp/index.d.ts +1 -1
- package/dist/commands/mcp/index.d.ts.map +1 -1
- package/dist/commands/mcp/index.js +3 -1
- package/dist/commands/mcp/index.js.map +1 -1
- package/dist/commands/memory/bridge.d.ts +1 -1
- package/dist/commands/memory/bridge.d.ts.map +1 -1
- package/dist/commands/memory/bridge.js +2 -6
- package/dist/commands/memory/bridge.js.map +1 -1
- package/dist/commands/memory/secret.d.ts.map +1 -1
- package/dist/commands/memory/secret.js +4 -3
- package/dist/commands/memory/secret.js.map +1 -1
- package/dist/commands/observe.d.ts +3 -3
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +11 -8
- package/dist/commands/observe.js.map +1 -1
- package/dist/commands/runbooks.d.ts.map +1 -1
- package/dist/commands/runbooks.js +77 -10
- package/dist/commands/runbooks.js.map +1 -1
- package/dist/commands/services-tls.d.ts.map +1 -1
- package/dist/commands/services-tls.js +41 -0
- package/dist/commands/services-tls.js.map +1 -1
- package/dist/commands/services.d.ts +45 -3
- package/dist/commands/services.d.ts.map +1 -1
- package/dist/commands/services.js +198 -71
- package/dist/commands/services.js.map +1 -1
- package/dist/commands/setup-phase-8-kg-hook.d.ts +48 -0
- package/dist/commands/setup-phase-8-kg-hook.d.ts.map +1 -0
- package/dist/commands/setup-phase-8-kg-hook.js +93 -0
- package/dist/commands/setup-phase-8-kg-hook.js.map +1 -0
- package/dist/commands/setup-phase-9-memory-bridge.d.ts +36 -0
- package/dist/commands/setup-phase-9-memory-bridge.d.ts.map +1 -0
- package/dist/commands/setup-phase-9-memory-bridge.js +59 -0
- package/dist/commands/setup-phase-9-memory-bridge.js.map +1 -0
- package/dist/commands/setup.d.ts +34 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +328 -23
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/update.d.ts +24 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +53 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +31 -8
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/image-digests.json +8 -8
- package/dist/index.js +4302 -2466
- package/dist/lib/auth-backend.d.ts +168 -0
- package/dist/lib/auth-backend.d.ts.map +1 -0
- package/dist/lib/auth-backend.js +172 -0
- package/dist/lib/auth-backend.js.map +1 -0
- package/dist/lib/auth-list-cache.d.ts +67 -0
- package/dist/lib/auth-list-cache.d.ts.map +1 -0
- package/dist/lib/auth-list-cache.js +84 -0
- package/dist/lib/auth-list-cache.js.map +1 -0
- package/dist/lib/auth-list.d.ts +107 -0
- package/dist/lib/auth-list.d.ts.map +1 -0
- package/dist/lib/auth-list.js +123 -0
- package/dist/lib/auth-list.js.map +1 -0
- package/dist/lib/auth-login.d.ts +92 -0
- package/dist/lib/auth-login.d.ts.map +1 -0
- package/dist/lib/auth-login.js +124 -0
- package/dist/lib/auth-login.js.map +1 -0
- package/dist/lib/auth-mutator-backend.d.ts +54 -0
- package/dist/lib/auth-mutator-backend.d.ts.map +1 -0
- package/dist/lib/auth-mutator-backend.js +62 -0
- package/dist/lib/auth-mutator-backend.js.map +1 -0
- package/dist/lib/auth-remote.d.ts +50 -0
- package/dist/lib/auth-remote.d.ts.map +1 -1
- package/dist/lib/auth-remote.js +84 -2
- package/dist/lib/auth-remote.js.map +1 -1
- package/dist/lib/bootstrap-kubernetes.d.ts +93 -12
- package/dist/lib/bootstrap-kubernetes.d.ts.map +1 -1
- package/dist/lib/bootstrap-kubernetes.js +364 -53
- package/dist/lib/bootstrap-kubernetes.js.map +1 -1
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/health-probes.d.ts +0 -22
- package/dist/lib/health-probes.d.ts.map +1 -1
- package/dist/lib/health-probes.js +23 -2
- package/dist/lib/health-probes.js.map +1 -1
- package/dist/lib/peripheral-registry.d.ts +11 -0
- package/dist/lib/peripheral-registry.d.ts.map +1 -1
- package/dist/lib/peripheral-registry.js +5 -0
- package/dist/lib/peripheral-registry.js.map +1 -1
- package/dist/lib/plans-client.d.ts.map +1 -1
- package/dist/lib/plans-client.js +6 -3
- package/dist/lib/plans-client.js.map +1 -1
- package/dist/mcp-server.js +14 -3
- package/hermes-bundle/version.json +1 -1
- package/host-cp/k8s/manifests/30-configmap.yaml +4 -0
- package/host-cp/k8s/manifests/50-deployment.yaml +13 -1
- package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
- package/host-cp/src/dispatch-persister.mjs +157 -0
- package/host-cp/src/pr-nanny.mjs +7 -0
- package/host-cp/src/server.mjs +175 -3
- package/host-cp/src/world-watchdog-pid-lookup.mjs +119 -0
- package/host-cp/src/world-watchdog-probes.mjs +271 -0
- package/host-cp/src/world-watchdog-recovery.mjs +192 -0
- package/host-cp/src/world-watchdog.mjs +313 -0
- package/package.json +1 -1
package/dist/commands/auth.js
CHANGED
|
@@ -39,7 +39,11 @@ import { readConfig } from '../lib/config.js';
|
|
|
39
39
|
import { applyK8sAuthRefresh, resolveKubectlContext, } from '../lib/auth-refresh-kubernetes.js';
|
|
40
40
|
import { remoteOAuthStart, remoteListServiceTokens, remoteListAccounts, remoteBindServiceToken, remoteDeleteServiceToken, remoteIssueAnthropicToken, remoteListAnthropicTokens, remoteRevokeAnthropicToken, runDoctorChecks, } from '../lib/auth-remote.js';
|
|
41
41
|
import { resolveCfAccessServiceToken } from '../lib/cf-access-token.js';
|
|
42
|
-
import { renderAuthListJson } from './auth-list-json.js';
|
|
42
|
+
import { renderAuthListJson, renderRemoteAuthListJson } from './auth-list-json.js';
|
|
43
|
+
import { runAuthLogin } from '../lib/auth-login.js';
|
|
44
|
+
import { runAuthList, } from '../lib/auth-list.js';
|
|
45
|
+
import { resolveMutatorBackend, } from '../lib/auth-mutator-backend.js';
|
|
46
|
+
import { registerAuthMigrate } from './auth-migrate.js';
|
|
43
47
|
/**
|
|
44
48
|
* Best-effort browser opener. Uses the host's native "open URL" command:
|
|
45
49
|
* `open` on macOS, `xdg-open` on Linux, `start` on Windows. Silently
|
|
@@ -57,6 +61,106 @@ function openBrowser(url) {
|
|
|
57
61
|
// Ignore — caller already printed the URL.
|
|
58
62
|
}
|
|
59
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* B4 (narrowed) — render an `olam auth list` result to stdout. Returns the
|
|
66
|
+
* desired exit code (0 = success; 1 = error). Pulled out of the action body
|
|
67
|
+
* so the orchestration in `runAuthList` is testable without driving the
|
|
68
|
+
* Commander.js scaffolding.
|
|
69
|
+
*/
|
|
70
|
+
function renderAuthList(result, opts) {
|
|
71
|
+
if (result.mode === 'error') {
|
|
72
|
+
if (opts.json) {
|
|
73
|
+
console.log(JSON.stringify({ error: result.message }));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
printError(result.message);
|
|
77
|
+
}
|
|
78
|
+
return result.exitCode;
|
|
79
|
+
}
|
|
80
|
+
if (result.mode === 'local') {
|
|
81
|
+
if (!result.reachable) {
|
|
82
|
+
if (opts.json) {
|
|
83
|
+
console.log(JSON.stringify({ error: 'auth-container-unreachable', reachable: false }));
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
printError('Auth container is not reachable. Run `olam services up` first.');
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
if (opts.json) {
|
|
90
|
+
console.log(renderAuthListJson(result.accounts));
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
printHeader(`Credentials (${result.accounts.length})`);
|
|
94
|
+
if (result.accounts.length === 0) {
|
|
95
|
+
console.log(` ${pc.dim('No credentials — run: olam auth login --label primary')}`);
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
const stateColor = (s) => {
|
|
99
|
+
if (s === 'active')
|
|
100
|
+
return pc.green('active');
|
|
101
|
+
if (s === 'cooldown')
|
|
102
|
+
return pc.yellow('cooldown');
|
|
103
|
+
if (s === 'expired')
|
|
104
|
+
return pc.red('expired');
|
|
105
|
+
if (s === 'disabled')
|
|
106
|
+
return pc.dim('disabled');
|
|
107
|
+
return pc.dim(s ?? 'unknown');
|
|
108
|
+
};
|
|
109
|
+
for (const a of result.accounts) {
|
|
110
|
+
const label = a.accountLabel ?? a.id;
|
|
111
|
+
const reqs = a.usage?.requestCount5h ?? 0;
|
|
112
|
+
const last429 = a.usage?.last429At
|
|
113
|
+
? `last429=${a.usage.last429At}`
|
|
114
|
+
: 'last429=never';
|
|
115
|
+
const reset = a.rateLimitResetsAt ? `resets=${a.rateLimitResetsAt}` : '';
|
|
116
|
+
console.log(` ${pc.bold(label.padEnd(18))} ${stateColor(a.state).padEnd(18)} ` +
|
|
117
|
+
`${pc.dim(`req5h=${reqs}`)} ${pc.dim(`exp=${a.expiresIn}`)} ${pc.dim(last429)} ${pc.yellow(reset)}`);
|
|
118
|
+
}
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
// remote
|
|
122
|
+
if (opts.json) {
|
|
123
|
+
console.log(renderRemoteAuthListJson(result.accounts, result.stale, result.fetchedAt));
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
const staleSuffix = result.stale ? pc.yellow(' (stale)') : '';
|
|
127
|
+
printHeader(`Remote accounts (${result.accounts.length})${staleSuffix}`);
|
|
128
|
+
if (result.accounts.length === 0) {
|
|
129
|
+
console.log(` ${pc.dim('No accounts — run: olam auth login --remote ' + result.baseUrl)}`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
for (const a of result.accounts) {
|
|
133
|
+
const label = a.label ?? a.id;
|
|
134
|
+
const state = a.state ?? 'unknown';
|
|
135
|
+
const exp = a.expiresIn ?? '';
|
|
136
|
+
console.log(` ${pc.bold(label.padEnd(20))} ${pc.green(state).padEnd(16)} ${pc.dim(exp)}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (result.stale) {
|
|
140
|
+
const fetchedAtIso = new Date(result.fetchedAt).toISOString();
|
|
141
|
+
console.log(`\n ${pc.yellow('warning:')} showing cached results from ${fetchedAtIso}; ` +
|
|
142
|
+
`fresh fetch failed (${result.fetchError ?? 'unknown error'}).`);
|
|
143
|
+
console.log(` ${pc.dim('Retry with --no-cache once connectivity is restored.')}`);
|
|
144
|
+
}
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* B4 (narrowed) — adapter around `resolveMutatorBackend` for the three
|
|
149
|
+
* local-only mutator subcommands. Prints the message body to stderr and
|
|
150
|
+
* returns the desired exit code (0 = proceed locally; non-zero = stop).
|
|
151
|
+
*/
|
|
152
|
+
function handleMutatorBackend(subcommand, opts) {
|
|
153
|
+
const outcome = resolveMutatorBackend(subcommand, opts);
|
|
154
|
+
if (outcome.outcome === 'proceed-local')
|
|
155
|
+
return 0;
|
|
156
|
+
if (outcome.outcome === 'conflict') {
|
|
157
|
+
process.stderr.write(`Error: ${outcome.message}\n`);
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
// remote-unsupported
|
|
161
|
+
process.stderr.write(`Error: ${outcome.message}\n`);
|
|
162
|
+
return outcome.exitCode;
|
|
163
|
+
}
|
|
60
164
|
async function promptLine(question) {
|
|
61
165
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
62
166
|
try {
|
|
@@ -98,9 +202,16 @@ export function registerAuth(program) {
|
|
|
98
202
|
});
|
|
99
203
|
auth
|
|
100
204
|
.command('disable')
|
|
101
|
-
.description('Take a credential out of rotation (manual cooldown)')
|
|
205
|
+
.description('Take a credential out of rotation (manual cooldown). LOCAL ONLY — no cloud equivalent yet (see OQ7 in docs/plans/cloud-only-vault/README.md).')
|
|
102
206
|
.argument('<label>', 'Credential label or id')
|
|
103
|
-
.
|
|
207
|
+
.option('--local', 'Explicit local backend (default — only backend supported today).')
|
|
208
|
+
.option('--remote [url]', 'Reserved — exits 2 with structured message pointing to OQ7.')
|
|
209
|
+
.action(async (label, opts) => {
|
|
210
|
+
const code = handleMutatorBackend('disable', opts);
|
|
211
|
+
if (code !== 0) {
|
|
212
|
+
process.exitCode = code;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
104
215
|
const client = new AuthClient();
|
|
105
216
|
try {
|
|
106
217
|
await client.disableAccount(label);
|
|
@@ -113,9 +224,16 @@ export function registerAuth(program) {
|
|
|
113
224
|
});
|
|
114
225
|
auth
|
|
115
226
|
.command('enable')
|
|
116
|
-
.description('Re-enable a disabled credential')
|
|
227
|
+
.description('Re-enable a disabled credential. LOCAL ONLY — no cloud equivalent yet (see OQ7 in docs/plans/cloud-only-vault/README.md).')
|
|
117
228
|
.argument('<label>', 'Credential label or id')
|
|
118
|
-
.
|
|
229
|
+
.option('--local', 'Explicit local backend (default — only backend supported today).')
|
|
230
|
+
.option('--remote [url]', 'Reserved — exits 2 with structured message pointing to OQ7.')
|
|
231
|
+
.action(async (label, opts) => {
|
|
232
|
+
const code = handleMutatorBackend('enable', opts);
|
|
233
|
+
if (code !== 0) {
|
|
234
|
+
process.exitCode = code;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
119
237
|
const client = new AuthClient();
|
|
120
238
|
try {
|
|
121
239
|
await client.enableAccount(label);
|
|
@@ -143,73 +261,85 @@ export function registerAuth(program) {
|
|
|
143
261
|
});
|
|
144
262
|
auth
|
|
145
263
|
.command('login')
|
|
146
|
-
.description('
|
|
264
|
+
.description('Log into the cloud auth-worker by default (Phase B); use --local to opt into the legacy local auth-service container PKCE flow.')
|
|
147
265
|
.option('--label <label>', 'Account label (e.g. primary, burner-1). Defaults to "primary" for the first account, "burner-N" thereafter.')
|
|
148
|
-
.option('--
|
|
266
|
+
.option('--local', 'Opt into the deprecated local auth-service container PKCE flow. Emits a deprecation warning.')
|
|
267
|
+
.option('--remote [url]', 'Force remote login. Optionally accepts an explicit auth-worker URL (back-compat); without a URL falls through to env/file/default discovery.')
|
|
268
|
+
.option('--yes, -y', 'Skip the first-time interactive confirm prompt before flipping to remote.')
|
|
149
269
|
.option('--print-url', 'Print the OAuth URL without attempting to open a browser (implied by --remote).')
|
|
150
270
|
.option('--service-token <client_id:secret>', 'Delegate to bind-service-token flow instead (use with --remote).')
|
|
151
271
|
.action(async (opts) => {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const clientIdStr = clientId ?? '';
|
|
175
|
-
try {
|
|
176
|
-
await remoteBindServiceToken({ baseUrl, cfAuthCookie: rawCookie }, { client_id: clientIdStr, label });
|
|
177
|
-
printSuccess(`Service token bound: ${clientIdStr} (label=${label})`);
|
|
178
|
-
console.log(`\n${pc.dim('Set ANTHROPIC_BASE' + '_URL=' + baseUrl + '/v1/proxy in your shell to route Claude API calls through this worker.')}`);
|
|
179
|
-
}
|
|
180
|
-
catch (err) {
|
|
181
|
-
printError(err instanceof Error ? err.message : 'bind failed');
|
|
182
|
-
process.exitCode = 1;
|
|
183
|
-
}
|
|
272
|
+
const result = await runAuthLogin(opts, {
|
|
273
|
+
promptConfirm: (question) => promptLine(question),
|
|
274
|
+
executeRemoteLogin: (baseUrl, o) => executeRemoteLogin(baseUrl, o),
|
|
275
|
+
executeLocalLogin: (o) => executeLocalLogin(o),
|
|
276
|
+
});
|
|
277
|
+
if (result.exitCode !== 0) {
|
|
278
|
+
process.exitCode = result.exitCode;
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
// ── Legacy login executors (extracted from the action body so runAuthLogin
|
|
282
|
+
// can drive them; behaviour preserved verbatim other than the --remote
|
|
283
|
+
// <url> form now arrives via the resolution-helper rather than opts.remote
|
|
284
|
+
// directly). ───────────────────────────────────────────────────────────────
|
|
285
|
+
async function executeRemoteLogin(baseUrl, opts) {
|
|
286
|
+
if (opts.serviceToken) {
|
|
287
|
+
// Delegate to bind-service-token flow
|
|
288
|
+
const parts = opts.serviceToken.split(':');
|
|
289
|
+
if (parts.length < 2) {
|
|
290
|
+
printError('--service-token must be in format <client_id>:<secret>');
|
|
291
|
+
process.exitCode = 1;
|
|
184
292
|
return;
|
|
185
293
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
294
|
+
const [clientId] = parts;
|
|
295
|
+
const label = opts.label ?? clientId ?? 'service-token';
|
|
296
|
+
printHeader('Auth worker — bind service token');
|
|
297
|
+
console.log(`\n ${pc.bold('Step 1:')} Open ${pc.cyan(baseUrl + '/v1/oauth/start')} in a browser.`);
|
|
298
|
+
console.log(` Complete CF Access SSO. You may see a JSON response or "no credentials" — that is normal.`);
|
|
299
|
+
const rawCookie = await promptLine(`\n ${pc.dim('Paste CF_Authorization cookie value (from DevTools > Application > Cookies):')} `);
|
|
300
|
+
if (!rawCookie) {
|
|
301
|
+
printError('No cookie provided. Aborting.');
|
|
302
|
+
process.exitCode = 1;
|
|
303
|
+
return;
|
|
194
304
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
305
|
+
const clientIdStr = clientId ?? '';
|
|
306
|
+
try {
|
|
307
|
+
await remoteBindServiceToken({ baseUrl, cfAuthCookie: rawCookie }, { client_id: clientIdStr, label });
|
|
308
|
+
printSuccess(`Service token bound: ${clientIdStr} (label=${label})`);
|
|
309
|
+
console.log(`\n${pc.dim('Set ANTHROPIC_BASE' + '_URL=' + baseUrl + '/v1/proxy in your shell to route Claude API calls through this worker.')}`);
|
|
198
310
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
console.log(` ${pc.bold('2.')} Complete the CF Access SSO and Anthropic OAuth consent.`);
|
|
203
|
-
console.log(` The worker stores your tokens on callback — the browser lands on a success page.`);
|
|
204
|
-
console.log(`\n ${pc.bold('3.')} Confirm your credentials are stored:\n`);
|
|
205
|
-
console.log(` ${pc.dim('olam auth list --remote ' + baseUrl)}\n`);
|
|
206
|
-
console.log(` ${pc.dim('Note: full CLI-orchestrated browser SSO flow is deferred to v2 (cookie-bridging complexity).')}\n`);
|
|
207
|
-
if (!opts.printUrl) {
|
|
208
|
-
openBrowser(startUrl);
|
|
311
|
+
catch (err) {
|
|
312
|
+
printError(err instanceof Error ? err.message : 'bind failed');
|
|
313
|
+
process.exitCode = 1;
|
|
209
314
|
}
|
|
210
315
|
return;
|
|
211
316
|
}
|
|
212
|
-
//
|
|
317
|
+
// Default: print (and optionally open) the OAuth start URL.
|
|
318
|
+
// V1 dogfood: browser completes the full flow (CF Access SSO → Anthropic
|
|
319
|
+
// OAuth → worker callback → token stored). CLI re-checks via `list --remote`.
|
|
320
|
+
// Full CLI-orchestrated cookie-bridging is deferred to v2.
|
|
321
|
+
let startUrl;
|
|
322
|
+
try {
|
|
323
|
+
const r = await remoteOAuthStart({ baseUrl });
|
|
324
|
+
startUrl = r.authorize_url;
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
// Fallback if /v1/oauth/start isn't reachable (CF Access gate, etc.)
|
|
328
|
+
startUrl = `${baseUrl.replace(/\/+$/, '')}/v1/oauth/start`;
|
|
329
|
+
}
|
|
330
|
+
printHeader('Auth worker — OAuth login (v1 dogfood)');
|
|
331
|
+
console.log(`\n ${pc.bold('1.')} Open this URL in your browser to authenticate via CF Access + Anthropic OAuth:`);
|
|
332
|
+
console.log(`\n ${pc.cyan(startUrl)}\n`);
|
|
333
|
+
console.log(` ${pc.bold('2.')} Complete the CF Access SSO and Anthropic OAuth consent.`);
|
|
334
|
+
console.log(` The worker stores your tokens on callback — the browser lands on a success page.`);
|
|
335
|
+
console.log(`\n ${pc.bold('3.')} Confirm your credentials are stored:\n`);
|
|
336
|
+
console.log(` ${pc.dim('olam auth list --remote ' + baseUrl)}\n`);
|
|
337
|
+
console.log(` ${pc.dim('Tokens written to cloud auth-worker. Full CLI-orchestrated browser SSO flow is deferred to v2.')}\n`);
|
|
338
|
+
if (!opts.printUrl) {
|
|
339
|
+
openBrowser(startUrl);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async function executeLocalLogin(opts) {
|
|
213
343
|
const preflight = await runAuthPreflight({ autoStart: true });
|
|
214
344
|
if (preflight.verdict !== 'ok' && preflight.verdict !== 'no-accounts') {
|
|
215
345
|
printError(preflight.message);
|
|
@@ -249,7 +379,7 @@ export function registerAuth(program) {
|
|
|
249
379
|
process.exitCode = 1;
|
|
250
380
|
return;
|
|
251
381
|
}
|
|
252
|
-
printHeader('Claude OAuth — PKCE flow');
|
|
382
|
+
printHeader('Claude OAuth — PKCE flow (legacy local container)');
|
|
253
383
|
console.log(`\n ${pc.bold('1.')} Opening Claude in your default browser…`);
|
|
254
384
|
console.log(` ${pc.dim(pending.loginUrl)}`);
|
|
255
385
|
openBrowser(pending.loginUrl);
|
|
@@ -265,13 +395,14 @@ export function registerAuth(program) {
|
|
|
265
395
|
try {
|
|
266
396
|
const result = await client.completeLogin(statePart, codePart);
|
|
267
397
|
printSuccess(`Account stored: ${result.account} (${result.expiresIn})`);
|
|
398
|
+
console.log(`${pc.dim('Tokens written to ~/.olam/auth-data/accounts.json (deprecated; cloud is the new default).')}`);
|
|
268
399
|
console.log(`\n${pc.dim('Next: olam create --name my-world')}`);
|
|
269
400
|
}
|
|
270
401
|
catch (err) {
|
|
271
402
|
printError(err instanceof Error ? err.message : 'token exchange failed');
|
|
272
403
|
process.exitCode = 1;
|
|
273
404
|
}
|
|
274
|
-
}
|
|
405
|
+
}
|
|
275
406
|
auth
|
|
276
407
|
.command('logout')
|
|
277
408
|
.description('Remove an account from the auth container')
|
|
@@ -289,9 +420,16 @@ export function registerAuth(program) {
|
|
|
289
420
|
});
|
|
290
421
|
auth
|
|
291
422
|
.command('refresh')
|
|
292
|
-
.description('Force-refresh an account token (substrate-aware: updates kubernetes Secret on k8s substrate)')
|
|
423
|
+
.description('Force-refresh an account token (substrate-aware: updates kubernetes Secret on k8s substrate). LOCAL ONLY — no cloud equivalent yet (see OQ7 in docs/plans/cloud-only-vault/README.md).')
|
|
293
424
|
.argument('<account>', 'Account id')
|
|
294
|
-
.
|
|
425
|
+
.option('--local', 'Explicit local backend (default — only backend supported today).')
|
|
426
|
+
.option('--remote [url]', 'Reserved — exits 2 with structured message pointing to OQ7.')
|
|
427
|
+
.action(async (accountId, opts) => {
|
|
428
|
+
const code = handleMutatorBackend('refresh', opts);
|
|
429
|
+
if (code !== 0) {
|
|
430
|
+
process.exitCode = code;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
295
433
|
const cfg = readConfig();
|
|
296
434
|
const isK8s = cfg.host.substrate === 'kubernetes';
|
|
297
435
|
// Kubernetes substrate: validate context before touching auth-service (SEC-NEW-003).
|
|
@@ -366,132 +504,49 @@ export function registerAuth(program) {
|
|
|
366
504
|
process.exitCode = 1;
|
|
367
505
|
}
|
|
368
506
|
});
|
|
369
|
-
// ──
|
|
507
|
+
// ── B4 (narrowed): list defaults to cloud auth-worker ────────────────────
|
|
370
508
|
auth
|
|
371
509
|
.command('list')
|
|
372
|
-
.description('List credentials (
|
|
373
|
-
.option('--
|
|
374
|
-
.option('--
|
|
375
|
-
.option('--
|
|
510
|
+
.description('List credentials. Defaults to the cloud auth-worker (Phase B). Pass --local to read the legacy ~/.olam/auth-data/accounts.json (emits deprecation warning).')
|
|
511
|
+
.option('--local', 'Read the legacy local auth-service vault. Emits a deprecation warning.')
|
|
512
|
+
.option('--remote [url]', 'Force remote. Optionally accepts an explicit auth-worker URL; without one falls through to env/file/default discovery.')
|
|
513
|
+
.option('--cookie <value>', 'CF_Authorization cookie for authenticated requests (remote path)')
|
|
514
|
+
.option('--no-cache', 'Bypass the 30 s TTL cache and force a fresh fetch (remote path)')
|
|
515
|
+
.option('--json', 'Emit machine-readable JSON instead of the text table', false)
|
|
376
516
|
.action(async (opts) => {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
catch (err) {
|
|
386
|
-
printWarning(`Could not fetch accounts: ${err instanceof Error ? err.message : String(err)}`);
|
|
387
|
-
}
|
|
388
|
-
try {
|
|
389
|
-
tokens = await remoteListServiceTokens(clientOpts);
|
|
390
|
-
}
|
|
391
|
-
catch (err) {
|
|
392
|
-
printWarning(`Could not fetch service tokens: ${err instanceof Error ? err.message : String(err)}`);
|
|
393
|
-
}
|
|
394
|
-
printHeader(`Remote accounts (${accounts.length})`);
|
|
395
|
-
if (accounts.length === 0) {
|
|
396
|
-
console.log(` ${pc.dim('No accounts — run: olam auth login --remote ' + baseUrl)}`);
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
for (const a of accounts) {
|
|
400
|
-
const label = a.label ?? a.id;
|
|
401
|
-
const state = a.state ?? 'unknown';
|
|
402
|
-
const exp = a.expiresIn ?? '';
|
|
403
|
-
console.log(` ${pc.bold(label.padEnd(20))} ${pc.green(state).padEnd(16)} ${pc.dim(exp)}`);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
printHeader(`Remote service tokens (${tokens.length})`);
|
|
407
|
-
if (tokens.length === 0) {
|
|
408
|
-
console.log(` ${pc.dim('No service tokens — run: olam auth bind-service-token --remote ' + baseUrl)}`);
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
for (const t of tokens) {
|
|
412
|
-
const label = t.label ?? t.client_id;
|
|
413
|
-
const created = t.created_at ?? '';
|
|
414
|
-
console.log(` ${pc.bold(label.padEnd(20))} ${pc.dim(t.client_id)} ${pc.dim(created)}`);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
// Local list (original behaviour)
|
|
420
|
-
const client = new AuthClient();
|
|
421
|
-
const status = await client.status();
|
|
422
|
-
if (!status.reachable) {
|
|
423
|
-
if (opts.json) {
|
|
424
|
-
// Machine-readable error so drivers never screen-scrape the
|
|
425
|
-
// human "not reachable" line. Exit code unchanged (1).
|
|
426
|
-
console.log(JSON.stringify({ error: 'auth-container-unreachable', reachable: false }));
|
|
427
|
-
process.exitCode = 1;
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
printError('Auth container is not reachable. Run `olam services up` first.');
|
|
431
|
-
process.exitCode = 1;
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
if (opts.json) {
|
|
435
|
-
console.log(renderAuthListJson(status.accounts));
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
printHeader(`Credentials (${status.accounts.length})`);
|
|
439
|
-
if (status.accounts.length === 0) {
|
|
440
|
-
console.log(` ${pc.dim('No credentials — run: olam auth login --label primary')}`);
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
const stateColor = (s) => {
|
|
444
|
-
if (s === 'active')
|
|
445
|
-
return pc.green('active');
|
|
446
|
-
if (s === 'cooldown')
|
|
447
|
-
return pc.yellow('cooldown');
|
|
448
|
-
if (s === 'expired')
|
|
449
|
-
return pc.red('expired');
|
|
450
|
-
if (s === 'disabled')
|
|
451
|
-
return pc.dim('disabled');
|
|
452
|
-
return pc.dim(s ?? 'unknown');
|
|
517
|
+
const listOpts = {
|
|
518
|
+
local: opts.local,
|
|
519
|
+
remote: opts.remote,
|
|
520
|
+
cookie: opts.cookie,
|
|
521
|
+
// commander.js maps `--no-cache` to `cache: false`. Translate.
|
|
522
|
+
noCache: opts.cache === false,
|
|
523
|
+
json: opts.json,
|
|
453
524
|
};
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
525
|
+
const result = await runAuthList(listOpts, {
|
|
526
|
+
fetchRemoteAccounts: async (baseUrl, cookie) => {
|
|
527
|
+
return remoteListAccounts({ baseUrl, cfAuthCookie: cookie });
|
|
528
|
+
},
|
|
529
|
+
fetchLocalStatus: () => new AuthClient().status(),
|
|
530
|
+
});
|
|
531
|
+
const exitCode = renderAuthList(result, listOpts);
|
|
532
|
+
if (exitCode !== 0)
|
|
533
|
+
process.exitCode = exitCode;
|
|
464
534
|
});
|
|
465
|
-
// ──
|
|
535
|
+
// ── B3: `olam auth migrate` (one-shot migration tool) ──────────────────
|
|
536
|
+
registerAuthMigrate(auth);
|
|
537
|
+
// ── Deprecation pointer: old `migrate-to-remote` → `migrate` ────────────
|
|
538
|
+
// Phase B3 replaces the e6 placeholder. Operators with scripted invocations
|
|
539
|
+
// of the old name see a one-line pointer + exit 0 (no script breakage); the
|
|
540
|
+
// real auto-migration lives at `olam auth migrate` now.
|
|
466
541
|
auth
|
|
467
542
|
.command('migrate-to-remote')
|
|
468
|
-
.description('
|
|
469
|
-
.
|
|
470
|
-
.action(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const status = await client.status();
|
|
476
|
-
accountLabels = status.accounts.map((a) => a.accountLabel ?? a.id);
|
|
477
|
-
}
|
|
478
|
-
catch {
|
|
479
|
-
// Auth container may not be running — proceed with generic guidance.
|
|
480
|
-
}
|
|
481
|
-
printHeader('Migrate local credentials to remote auth-worker');
|
|
482
|
-
console.log(`\n Remote URL: ${pc.cyan(baseUrl)}\n`);
|
|
483
|
-
console.log(` ${pc.dim('V1 note: secrets cannot be migrated automatically. Each account must re-authenticate via OAuth.')}\n`);
|
|
484
|
-
if (accountLabels.length === 0) {
|
|
485
|
-
console.log(` No local accounts found. Run:`);
|
|
486
|
-
console.log(`\n ${pc.cyan('olam auth login --remote ' + baseUrl)}\n`);
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
console.log(` Found ${accountLabels.length} local account(s). For each, run:\n`);
|
|
490
|
-
for (const label of accountLabels) {
|
|
491
|
-
console.log(` ${pc.cyan('olam auth login --remote ' + baseUrl + ' --label ' + label)}`);
|
|
492
|
-
}
|
|
493
|
-
console.log();
|
|
494
|
-
}
|
|
543
|
+
.description('(deprecated) renamed to `olam auth migrate`. Prints pointer + exits 0.')
|
|
544
|
+
.option('--url <url>', 'Auth-worker base URL (ignored; pass --remote to `migrate` instead)')
|
|
545
|
+
.action(() => {
|
|
546
|
+
printHeader('migrate-to-remote has been renamed');
|
|
547
|
+
console.log(`\n Run ${pc.cyan('olam auth migrate --help')} for the new auto-migration flow.\n` +
|
|
548
|
+
` (B3 of cloud-only-vault: per-account re-OAuth against the cloud auth-worker,\n` +
|
|
549
|
+
` content-hash idempotent, atomic state writes, --dry-run preview.)\n`);
|
|
495
550
|
});
|
|
496
551
|
// ── e6: rotate-service-token ───────────────────────────────────────────
|
|
497
552
|
auth
|