@thinkingai/ae-cli 6.0.47 → 6.0.48

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 (80) hide show
  1. package/README.md +93 -36
  2. package/README.zh.md +93 -36
  3. package/dist/{auth-QPEVVBKT.js → auth-FUM37MHF.js} +236 -127
  4. package/dist/{capability-RUSV77CO.js → capability-J4FU4JZW.js} +47 -34
  5. package/dist/{chunk-FR2EDAQM.js → chunk-4P355ZWB.js} +69 -1
  6. package/dist/{chunk-NSIVXKJH.js → chunk-6KBBAPPP.js} +5 -5
  7. package/dist/chunk-6ZIQV5GW.js +26 -0
  8. package/dist/chunk-ATSM5XAW.js +623 -0
  9. package/dist/{chunk-6QK7SYYY.js → chunk-BBEFP4SB.js} +29 -37
  10. package/dist/{chunk-KEWVSO64.js → chunk-CZU3V3DQ.js} +4 -15
  11. package/dist/chunk-ECBLHAZO.js +15 -0
  12. package/dist/{chunk-BVDST5YT.js → chunk-HPLNYQPM.js} +8 -8
  13. package/dist/{chunk-B5HMQAOM.js → chunk-I4WQAEYB.js} +1 -1
  14. package/dist/{chunk-QL6R6TSK.js → chunk-LNZBEQXW.js} +6 -5
  15. package/dist/{chunk-GJXRF7Y6.js → chunk-QJQZH5GQ.js} +47 -78
  16. package/dist/{chunk-JYMGHGCE.js → chunk-VXNMYUXU.js} +24 -13
  17. package/dist/{chunk-KZ6ZHF5J.js → chunk-WZ6YVQSF.js} +14 -14
  18. package/dist/{community-report-client-6OL6GBQW.js → community-report-client-XXWGSBSD.js} +2 -4
  19. package/dist/{config-XNMACCKX.js → config-EXUGQN5T.js} +8 -9
  20. package/dist/{data-integration-4NZGZ45E.js → data-integration-4NZ53OMT.js} +17 -15
  21. package/dist/index.js +52 -79
  22. package/dist/{local-data-upload-client-EFHDLXFG.js → local-data-upload-client-KYOKVYZV.js} +2 -4
  23. package/dist/{memory-HBJQ2GC5.js → memory-ATNKZNW5.js} +4 -6
  24. package/dist/{metadata-PGDJE5RQ.js → metadata-3J5ZAVMF.js} +7 -9
  25. package/dist/{model-BHLJ2QMT.js → model-E4JMQ4V2.js} +4 -6
  26. package/dist/{personal-semantic-preference-E5NZ56DN.js → personal-semantic-preference-32AAJ37M.js} +6 -8
  27. package/dist/{sync-MUWPRM5I.js → sync-2GTAKZYX.js} +9 -11
  28. package/dist/{te-agent-WQZTMEK4.js → te-agent-JB5T3PO7.js} +393 -90
  29. package/dist/{te-analysis-YWCSSLYL.js → te-analysis-WHYXHRJD.js} +97 -31
  30. package/dist/{te-community-BNE6KREL.js → te-community-UDBI672N.js} +9 -32
  31. package/dist/{te-dataops-TXLKM2W6.js → te-dataops-ZLYOCXZ4.js} +19 -20
  32. package/dist/{te-engage-VEW4CJHK.js → te-engage-DHKNJ5BO.js} +11 -13
  33. package/dist/{te-kb-BUD6N3NI.js → te-kb-WYQWHFSC.js} +595 -85
  34. package/dist/{te-system-44LZJDTQ.js → te-system-7G6F2LJA.js} +6 -8
  35. package/dist/{te-team-XMP47YXE.js → te-team-E7FBBXMQ.js} +6 -8
  36. package/dist/{update-UODFOZ4P.js → update-D47BUG25.js} +7 -8
  37. package/package.json +12 -9
  38. package/skills/ae-agent/SKILL.md +30 -13
  39. package/skills/ae-agent/references/agent-distribution.md +94 -0
  40. package/skills/ae-agent/references/approval-request.md +4 -0
  41. package/skills/ae-agent/references/command_index.md +9 -2
  42. package/skills/ae-agent/references/create-automation.md +20 -2
  43. package/skills/ae-agent/references/get-agent-context.md +70 -0
  44. package/skills/ae-agent/references/list-automations.md +18 -3
  45. package/skills/ae-agent/references/list-mcp-credentials.md +1 -1
  46. package/skills/ae-agent/references/mcp-token.md +3 -3
  47. package/skills/ae-agent/references/set-mcp-credential.md +0 -1
  48. package/skills/ae-agent/references/update-automation.md +18 -0
  49. package/skills/ae-analysis/SKILL.md +11 -2
  50. package/skills/ae-analysis/references/adhoc_run.md +2 -0
  51. package/skills/ae-analysis/references/ai_models.md +23 -3
  52. package/skills/ae-analysis/references/analysis_gateway_assets.md +2 -2
  53. package/skills/ae-analysis/references/command_index.md +7 -7
  54. package/skills/ae-analysis/references/cross_source_config.md +84 -0
  55. package/skills/ae-analysis/references/dashboard_update.md +1 -1
  56. package/skills/ae-analysis/references/user_tag_create.md +23 -1
  57. package/skills/ae-analysis/references/user_tag_models.md +9 -1
  58. package/skills/ae-analysis/references/user_tag_update.md +14 -2
  59. package/skills/ae-data-integration/SKILL.md +2 -1
  60. package/skills/ae-data-integration/references/dimension-routing.md +36 -0
  61. package/skills/ae-data-integration/references/local-analysis.md +2 -0
  62. package/skills/ae-data-integration/references/source-inspect.md +1 -1
  63. package/skills/ae-data-integration/references/tracking-plan.md +1 -1
  64. package/skills/ae-data-integration/references/transform.md +1 -1
  65. package/skills/ae-data-integration/references/ue-mapping.md +5 -2
  66. package/skills/ae-data-integration/references/ue-routing.md +17 -1
  67. package/skills/ae-generate-tracking-plan/SKILL.md +25 -13
  68. package/skills/ae-generate-tracking-plan/references/business-dimension-mapping.md +1 -1
  69. package/skills/ae-kb/SKILL.md +107 -36
  70. package/skills/ae-kb/references/query-workflow.md +59 -29
  71. package/skills/ae-kb/references/versions.md +46 -0
  72. package/skills/ae-system/SKILL.md +1 -1
  73. package/skills/ae-use-agent/SKILL.md +42 -0
  74. package/skills/ae-use-agent/references/local-agent.md +114 -0
  75. package/dist/auth-UMJAA72O.js +0 -15
  76. package/dist/chunk-533JK3YX.js +0 -72
  77. package/dist/chunk-O6K2ODUE.js +0 -318
  78. package/dist/chunk-RWXIXOZG.js +0 -326
  79. package/dist/client-N7NGZUNI.js +0 -204
  80. package/skills/ae-agent/references/auto-provision-mcp-credentials.md +0 -57
@@ -1,39 +1,47 @@
1
1
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
2
+ import {
3
+ MultiselectCancelled,
4
+ promptSingleSelect
5
+ } from "./chunk-7K24F7T2.js";
6
+ import {
7
+ missingAeHostGuidance,
8
+ missingAeHostHint
9
+ } from "./chunk-6ZIQV5GW.js";
2
10
  import {
3
11
  getLocalCliPackageInfo
4
12
  } from "./chunk-E2JKXMVO.js";
13
+ import {
14
+ resolveHost
15
+ } from "./chunk-ECBLHAZO.js";
5
16
  import {
6
17
  printError,
7
18
  printOutput,
8
19
  runHostCompatCheck
9
- } from "./chunk-KEWVSO64.js";
20
+ } from "./chunk-CZU3V3DQ.js";
10
21
  import {
11
22
  CliTokenValidationUnavailableError,
12
- clearCliToken,
23
+ SecureStoreAuthError,
24
+ activateCredential,
25
+ clearAllCredentials,
26
+ clearCliTokenCache,
27
+ getActiveCredential,
28
+ listCredentials,
13
29
  mintCliToken,
30
+ removeActiveCredential,
31
+ saveCredential,
32
+ updateCredentialMetadata,
14
33
  validateCliTokenOnServer
15
- } from "./chunk-RWXIXOZG.js";
16
- import {
17
- SecureStoreAuthError,
18
- clear,
19
- load,
20
- missingAeHostGuidance,
21
- missingAeHostHint,
22
- resolveHost,
23
- save
24
- } from "./chunk-O6K2ODUE.js";
34
+ } from "./chunk-ATSM5XAW.js";
25
35
  import {
26
36
  getFallbackCliToken,
27
37
  loadConfig,
28
38
  normalizeUrl,
29
39
  saveConfig
30
- } from "./chunk-KZ6ZHF5J.js";
31
- import {
32
- PermissionError
33
- } from "./chunk-533JK3YX.js";
40
+ } from "./chunk-WZ6YVQSF.js";
34
41
  import {
42
+ PermissionError,
35
43
  logger
36
- } from "./chunk-FR2EDAQM.js";
44
+ } from "./chunk-4P355ZWB.js";
37
45
 
38
46
  // src/core/device-auth.ts
39
47
  import { execFileSync } from "child_process";
@@ -263,55 +271,141 @@ function resolveAuthHost(program, opts) {
263
271
  }
264
272
  async function validateExistingCliToken(host, cliToken) {
265
273
  try {
266
- await validateCliTokenOnServer(host, cliToken);
267
- return true;
274
+ return { valid: true, trusted: false, details: await validateCliTokenOnServer(host, cliToken) };
268
275
  } catch (error) {
269
276
  if (error instanceof CliTokenValidationUnavailableError) {
270
277
  logger.warn(`auth status: ${error.message}; trusting the stored CLI token for compatibility`);
271
- return true;
272
- }
273
- if (!(error instanceof SecureStoreAuthError)) {
274
- throw error;
278
+ return { valid: true, trusted: true, details: {} };
275
279
  }
280
+ if (!(error instanceof SecureStoreAuthError)) throw error;
276
281
  printError("auth", error.message);
277
282
  process.exitCode = 1;
278
- return false;
283
+ return { valid: false, trusted: false, details: {} };
279
284
  }
280
285
  }
281
286
  function activateHostAfterLogin(host, explicitHostOverride) {
282
287
  if (!explicitHostOverride) return;
283
288
  const normalized = normalizeUrl(host);
284
289
  const config = loadConfig();
285
- if (!config.hosts[normalized]) {
286
- config.hosts[normalized] = { label: normalized };
287
- }
290
+ if (!config.hosts[normalized]) config.hosts[normalized] = { label: normalized };
288
291
  config.activeHost = normalized;
289
292
  saveConfig(config);
290
293
  logger.info(`Active host set to ${normalized} after login`);
291
294
  }
292
- async function persistDeviceTokens(host, tokens) {
293
- const DEFAULT_EXPIRES_IN = 72e3;
294
- const MAX_EXPIRES_IN = 86400 * 365;
295
- let expiresInSeconds = tokens.expires_in ?? DEFAULT_EXPIRES_IN;
296
- if (!Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0 || expiresInSeconds > MAX_EXPIRES_IN) {
297
- logger.warn(`auth login: expires_in=${tokens.expires_in} is out of range, falling back to ${DEFAULT_EXPIRES_IN}s`);
298
- expiresInSeconds = DEFAULT_EXPIRES_IN;
299
- }
300
- const expiresAt = new Date(Date.now() + expiresInSeconds * 1e3).toISOString();
301
- const cliToken = await mintCliToken(host, tokens.access_token, false);
302
- save(host, {
303
- accessToken: tokens.access_token,
304
- refreshToken: tokens.refresh_token ?? "",
305
- accessExpiresAt: expiresAt,
306
- cliToken
295
+ function accountOutput(account) {
296
+ return {
297
+ open_id: account.openId,
298
+ login_name: account.loginName,
299
+ user_name: account.userName
300
+ };
301
+ }
302
+ function credentialOutput(credential, active) {
303
+ return {
304
+ credential_id: credential.id,
305
+ active,
306
+ ...credential.account ? { account: accountOutput(credential.account) } : {},
307
+ cli_token: {
308
+ ...credential.cliTokenExpiresAt ? { expires_at: credential.cliTokenExpiresAt } : {},
309
+ ...credential.lastRenewedOn ? { last_renewed_on: credential.lastRenewedOn } : {}
310
+ }
311
+ };
312
+ }
313
+ function buildAccountSelectionItems(credentials, activeCredentialId) {
314
+ return credentials.filter((credential) => Boolean(credential.account)).sort((left, right) => Number(right.id === activeCredentialId) - Number(left.id === activeCredentialId)).map((credential) => {
315
+ const account = credential.account;
316
+ const displayName = account.userName && account.userName !== account.loginName ? `${account.loginName} (${account.userName})` : account.loginName;
317
+ const active = credential.id === activeCredentialId ? " [active]" : "";
318
+ return {
319
+ value: account.openId,
320
+ label: `${displayName} \xB7 ${account.openId}${active}`
321
+ };
307
322
  });
323
+ }
324
+ async function switchActiveAccount(program, opts) {
325
+ const host = resolveAuthHost(program, opts);
326
+ if (!host) {
327
+ printError("config", "No AE host configured.", missingAeHostHint());
328
+ process.exitCode = 1;
329
+ return;
330
+ }
331
+ let account = opts.account;
332
+ if (!account) {
333
+ if (!process.stdin.isTTY || !process.stderr.isTTY) {
334
+ printError(
335
+ "validation",
336
+ "Interactive account selection requires a TTY.",
337
+ `Run: ae-cli auth list --host ${host}, then ae-cli auth use --host ${host} --account <login-name-or-open-id>`
338
+ );
339
+ process.exitCode = 1;
340
+ return;
341
+ }
342
+ const active = getActiveCredential(host);
343
+ const items = buildAccountSelectionItems(listCredentials(host), active?.id);
344
+ if (items.length === 0) {
345
+ printError(
346
+ "auth",
347
+ `No stored accounts with identity metadata for ${host}.`,
348
+ `Upgrade the AE server if needed, then run: ae-cli auth login --host ${host}`
349
+ );
350
+ process.exitCode = 1;
351
+ return;
352
+ }
353
+ try {
354
+ account = await promptSingleSelect({
355
+ title: `Select an account for ${host} (up/down: move, enter: select, q: cancel)`,
356
+ items
357
+ });
358
+ } catch (error) {
359
+ if (error instanceof MultiselectCancelled) return;
360
+ throw error;
361
+ }
362
+ }
363
+ try {
364
+ const selected = activateCredential(host, account);
365
+ clearCliTokenCache(host);
366
+ await printOutput({
367
+ host,
368
+ active: true,
369
+ account: accountOutput(selected.account)
370
+ }, program.opts().format || "json");
371
+ } catch (error) {
372
+ printError("auth", error.message);
373
+ process.exitCode = 1;
374
+ }
375
+ }
376
+ async function persistDeviceTokens(host, tokens, options = {}) {
377
+ const cliToken = await mintCliToken(host, tokens.access_token);
378
+ let validation = {};
379
+ try {
380
+ validation = await validateCliTokenOnServer(host, cliToken);
381
+ } catch (error) {
382
+ if (error instanceof CliTokenValidationUnavailableError) {
383
+ logger.warn(`auth login: ${error.message}; saving CLI token without account metadata for compatibility`);
384
+ } else {
385
+ clearCliTokenCache(host);
386
+ throw error;
387
+ }
388
+ }
389
+ let saved;
390
+ try {
391
+ saved = saveCredential(host, {
392
+ cliToken,
393
+ ...validation.account ? { account: validation.account } : {},
394
+ ...validation.cliTokenExpiresAt ? { cliTokenExpiresAt: validation.cliTokenExpiresAt } : {}
395
+ }, options);
396
+ } catch (error) {
397
+ clearCliTokenCache(host);
398
+ throw error;
399
+ }
400
+ clearCliTokenCache(host);
308
401
  await runHostCompatCheck(getLocalCliPackageInfo(), host);
402
+ return saved;
309
403
  }
310
- function loginSummary(host, tokens) {
404
+ function loginSummary(host, credential) {
311
405
  return {
312
406
  authenticated: true,
313
407
  host,
314
- token: tokens.access_token.slice(0, 8) + "..."
408
+ ...credential.account ? { account: accountOutput(credential.account) } : {}
315
409
  };
316
410
  }
317
411
  function resolveLoginTeClaudeBase(host) {
@@ -320,8 +414,10 @@ function resolveLoginTeClaudeBase(host) {
320
414
  return base.endsWith(basePath) ? base : base + basePath;
321
415
  }
322
416
  function registerAuth(program) {
323
- const auth = program.command("auth").description("Authentication management");
324
- auth.command("login").description("Log in to AE using the device code flow (cross-platform; --no-browser to print URL only)").option("--host <url>", HOST_OPTION_DESC).option("--no-browser", "Do not auto-open the browser; only print the authorization URL (headless / Linux)").option("--no-wait", "Request the device code and print the authorization URL as JSON, then exit without polling (for AI agents / split-flow). Resume with --device-code").option("--device-code <code>", "Resume a split-flow login: poll for the given device code until authorized, then save the token").action(async (opts) => {
417
+ const auth = program.command("auth").description("Manage authentication or switch the active account interactively").action(async () => {
418
+ await switchActiveAccount(program, {});
419
+ });
420
+ auth.command("login").description("Log in to AE; by default replaces this host credentials, --add keeps existing accounts").option("--host <url>", HOST_OPTION_DESC).option("--add", "Add another account for the host and keep existing accounts").option("--no-browser", "Do not auto-open the browser; only print the authorization URL").option("--no-wait", "Print device authorization data and exit; resume with --device-code").option("--device-code <code>", "Resume a split-flow login with the given device code").action(async (opts) => {
325
421
  const explicitHost = getExplicitAuthHostOverride(program, opts);
326
422
  const host = resolveAuthHost(program, opts);
327
423
  if (!host) {
@@ -331,135 +427,148 @@ function registerAuth(program) {
331
427
  const config = loadConfig();
332
428
  if (!config.hosts[host]) {
333
429
  config.hosts[host] = { label: host };
334
- if (!config.activeHost) {
335
- config.activeHost = host;
336
- }
430
+ if (!config.activeHost) config.activeHost = host;
337
431
  saveConfig(config);
338
- logger.info(`Host config auto-saved for ${host}`);
339
432
  process.stderr.write(`[ae-cli] Config saved for ${host}
340
433
  `);
341
434
  }
342
435
  const noBrowser = !opts.browser;
343
- const fmt = program.opts().format || "json";
436
+ const format = program.opts().format || "json";
344
437
  const teClaudeBase = resolveLoginTeClaudeBase(host);
345
- const emit = (msg) => process.stderr.write(`[ae-cli] ${msg}
438
+ const emit = (message) => process.stderr.write(`[ae-cli] ${message}
346
439
  `);
347
440
  try {
348
441
  if (opts.deviceCode) {
349
442
  const tokens2 = await pollDeviceFlow(teClaudeBase, opts.deviceCode, {}, emit);
350
- await persistDeviceTokens(host, tokens2);
443
+ const saved2 = await persistDeviceTokens(host, tokens2, { add: opts.add });
351
444
  activateHostAfterLogin(host, explicitHost);
352
- logger.info(`Device flow login successful for ${host} (resumed)`);
353
- emit("Login successful! Token saved securely.");
354
- await printOutput(loginSummary(host, tokens2), fmt);
445
+ emit("Login successful! CLI token saved securely.");
446
+ await printOutput(loginSummary(host, saved2), format);
355
447
  return;
356
448
  }
357
449
  if (opts.wait === false) {
358
450
  const authResp = await authorizeDevice(teClaudeBase);
359
- await printOutput(
360
- {
361
- device_code: authResp.device_code,
362
- user_code: authResp.user_code,
363
- verification_url: buildVerificationUrl(teClaudeBase, authResp.user_code),
364
- interval: authResp.interval,
365
- expires_in: authResp.expires_in,
366
- next: `Open verification_url in a browser to authorize, then run: ae-cli auth login --device-code ${authResp.device_code}`
367
- },
368
- fmt
369
- );
451
+ const addFlag = opts.add ? " --add" : "";
452
+ await printOutput({
453
+ device_code: authResp.device_code,
454
+ user_code: authResp.user_code,
455
+ verification_url: buildVerificationUrl(teClaudeBase, authResp.user_code),
456
+ interval: authResp.interval,
457
+ expires_in: authResp.expires_in,
458
+ next: `Open verification_url in a browser to authorize, then run: ae-cli auth login${addFlag} --device-code ${authResp.device_code}`
459
+ }, format);
370
460
  return;
371
461
  }
372
462
  const tokens = await runDeviceFlow(teClaudeBase, { noBrowser }, emit);
373
- await persistDeviceTokens(host, tokens);
463
+ const saved = await persistDeviceTokens(host, tokens, { add: opts.add });
374
464
  activateHostAfterLogin(host, explicitHost);
375
- logger.info(`Device flow login successful for ${host}`);
376
- emit("Login successful! Token saved securely.");
377
- await printOutput(loginSummary(host, tokens), fmt);
378
- } catch (err) {
379
- if (err instanceof DeviceFlowUnsupportedError) {
380
- printError(
381
- "auth",
382
- err.message,
383
- "Upgrade the AE server to a version that supports device login."
384
- );
465
+ emit("Login successful! CLI token saved securely.");
466
+ await printOutput(loginSummary(host, saved), format);
467
+ } catch (error) {
468
+ if (error instanceof DeviceFlowUnsupportedError) {
469
+ printError("auth", error.message, "Upgrade the AE server to a version that supports device login.");
385
470
  process.exit(1);
386
471
  }
387
- if (err instanceof PermissionError) {
388
- printError("permission", err.message, err.hint, err.code);
472
+ if (error instanceof PermissionError) {
473
+ printError("permission", error.message, error.hint, error.code);
389
474
  process.exit(1);
390
475
  }
391
- printError("auth", err.message);
476
+ printError("auth", error.message);
392
477
  process.exit(1);
393
478
  }
394
479
  });
395
- auth.command("status").description("Show authentication status for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
480
+ auth.command("status").description("Show the active CLI-token authentication status for a host").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
396
481
  const host = resolveAuthHost(program, opts);
397
482
  if (!host) {
398
- await printOutput(
399
- {
400
- authenticated: false,
401
- host: "(none)",
402
- hint: missingAeHostHint(),
403
- next_steps: missingAeHostGuidance()
404
- },
405
- program.opts().format || "json"
406
- );
483
+ await printOutput({
484
+ authenticated: false,
485
+ host: "(none)",
486
+ hint: missingAeHostHint(),
487
+ next_steps: missingAeHostGuidance()
488
+ }, program.opts().format || "json");
407
489
  return;
408
490
  }
409
- const secureEntry = load(host);
410
- if (secureEntry) {
411
- if (secureEntry.cliToken && !await validateExistingCliToken(host, secureEntry.cliToken)) {
412
- return;
491
+ const active = getActiveCredential(host);
492
+ if (active) {
493
+ const state = await validateExistingCliToken(host, active.cliToken);
494
+ if (!state.valid) return;
495
+ if (state.details.account || state.details.cliTokenExpiresAt) {
496
+ updateCredentialMetadata(host, active.cliToken, state.details);
413
497
  }
414
- const pastStaticExpiry = new Date(secureEntry.accessExpiresAt).getTime() < Date.now();
415
- await printOutput(
416
- {
417
- authenticated: true,
418
- host,
419
- source: "secure-store",
420
- accessExpiresAt: secureEntry.accessExpiresAt,
421
- accessExpiresAtNote: "advisory: access token auto-slides on use server-side; re-login only if a command returns an auth error",
422
- pastStaticExpiry,
423
- hasCliToken: !!secureEntry.cliToken
498
+ const current = getActiveCredential(host) ?? active;
499
+ await printOutput({
500
+ authenticated: true,
501
+ host,
502
+ source: "secure-store",
503
+ cli_token: {
504
+ status: state.trusted ? "trusted" : "valid",
505
+ ...current.cliTokenExpiresAt ? { expires_at: current.cliTokenExpiresAt } : {},
506
+ ...current.lastRenewedOn ? { last_renewed_on: current.lastRenewedOn } : {}
424
507
  },
425
- program.opts().format || "json"
426
- );
508
+ ...current.account ? { account: accountOutput(current.account) } : {}
509
+ }, program.opts().format || "json");
427
510
  return;
428
511
  }
429
- const fallbackCliToken = getFallbackCliToken(host);
430
- if (fallbackCliToken) {
431
- if (!await validateExistingCliToken(host, fallbackCliToken)) {
432
- return;
433
- }
434
- await printOutput(
435
- {
436
- authenticated: true,
437
- host,
438
- source: "sandbox-cli-token",
439
- hasCliToken: true,
440
- note: "sandbox-provisioned cli-token.json; no user access token in secure-store"
512
+ const fallback = getFallbackCliToken(host);
513
+ if (fallback) {
514
+ const state = await validateExistingCliToken(host, fallback);
515
+ if (!state.valid) return;
516
+ await printOutput({
517
+ authenticated: true,
518
+ host,
519
+ source: "sandbox-cli-token",
520
+ cli_token: {
521
+ status: state.trusted ? "trusted" : "valid",
522
+ ...state.details.cliTokenExpiresAt ? { expires_at: state.details.cliTokenExpiresAt } : {}
441
523
  },
442
- program.opts().format || "json"
443
- );
524
+ ...state.details.account ? { account: accountOutput(state.details.account) } : {}
525
+ }, program.opts().format || "json");
444
526
  return;
445
527
  }
446
528
  await printOutput({ authenticated: false, host, source: "none" }, program.opts().format || "json");
447
529
  });
448
- auth.command("logout").description("Clear stored credentials (access token, refresh token, and CLI token) for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
530
+ auth.command("list").description("List stored accounts for one host (does not call the server)").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
449
531
  const host = resolveAuthHost(program, opts);
450
532
  if (!host) {
451
533
  printError("config", "No AE host configured.", missingAeHostHint());
452
534
  process.exit(1);
453
535
  }
454
- clearCliToken(host);
455
- clear(host);
456
- process.stderr.write(`[ae-cli] Credentials cleared for ${host}
457
- `);
458
- await printOutput({ cleared: true, host }, program.opts().format || "json");
536
+ const active = getActiveCredential(host);
537
+ const credentials = listCredentials(host);
538
+ await printOutput({
539
+ host,
540
+ accounts: credentials.map((credential) => credentialOutput(credential, credential.id === active?.id))
541
+ }, program.opts().format || "json");
542
+ });
543
+ auth.command("use").description("Switch the active account for a host; omit --account to choose interactively").option("--account <login-name-or-open-id>", "Login name or open_id returned by auth list").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
544
+ await switchActiveAccount(program, opts);
545
+ });
546
+ auth.command("logout").description("Remove the active account, or all accounts with --all").option("--host <url>", HOST_OPTION_DESC).option("--all", "Remove all accounts stored for this host").action(async (opts) => {
547
+ const host = resolveAuthHost(program, opts);
548
+ if (!host) {
549
+ printError("config", "No AE host configured.", missingAeHostHint());
550
+ process.exit(1);
551
+ }
552
+ clearCliTokenCache(host);
553
+ if (opts.all) {
554
+ clearAllCredentials(host);
555
+ await printOutput({ cleared: true, all: true, host }, program.opts().format || "json");
556
+ return;
557
+ }
558
+ const result = removeActiveCredential(host);
559
+ await printOutput({
560
+ cleared: !!result.removed,
561
+ all: false,
562
+ host,
563
+ remaining_accounts: listCredentials(host).length,
564
+ ...result.active?.account ? { active_account: accountOutput(result.active.account) } : {}
565
+ }, program.opts().format || "json");
459
566
  });
460
567
  }
461
568
  export {
462
569
  activateHostAfterLogin,
570
+ buildAccountSelectionItems,
571
+ persistDeviceTokens,
463
572
  registerAuth,
464
573
  resolveLoginTeClaudeBase
465
574
  };
@@ -3,36 +3,38 @@ import {
3
3
  normalizeRiskLevel,
4
4
  requiresConfirmation
5
5
  } from "./chunk-AMBFK2K3.js";
6
- import {
7
- printError,
8
- printOutput
9
- } from "./chunk-KEWVSO64.js";
10
- import {
11
- CapabilityGatewayError,
12
- dryRunCapability,
13
- executeCapability,
14
- inspectCapability,
15
- listCapabilities,
16
- validateCapability
17
- } from "./chunk-GJXRF7Y6.js";
18
6
  import {
19
7
  findGatewayDomain,
20
8
  listRegisteredCapabilityDomains,
21
9
  resolveGatewayDomain
22
10
  } from "./chunk-TKHSULJT.js";
23
- import "./chunk-Y74WTIKJ.js";
24
- import "./chunk-RWXIXOZG.js";
25
11
  import {
26
- SecureStoreAuthError,
27
12
  resolveHost
28
- } from "./chunk-O6K2ODUE.js";
13
+ } from "./chunk-ECBLHAZO.js";
14
+ import {
15
+ CapabilityGatewayError,
16
+ dryRunCapabilityWithEnvelope,
17
+ executeCapabilityWithEnvelope,
18
+ inspectCapability,
19
+ inspectCapabilityWithEnvelope,
20
+ listCapabilitiesWithEnvelope,
21
+ validateCapabilityWithEnvelope
22
+ } from "./chunk-QJQZH5GQ.js";
23
+ import {
24
+ printError,
25
+ printOutput,
26
+ withOutputMetadata
27
+ } from "./chunk-CZU3V3DQ.js";
28
+ import "./chunk-Y74WTIKJ.js";
29
+ import {
30
+ SecureStoreAuthError
31
+ } from "./chunk-ATSM5XAW.js";
29
32
  import {
30
33
  safeJsonParse
31
- } from "./chunk-KZ6ZHF5J.js";
34
+ } from "./chunk-WZ6YVQSF.js";
32
35
  import {
33
36
  PermissionError
34
- } from "./chunk-533JK3YX.js";
35
- import "./chunk-FR2EDAQM.js";
37
+ } from "./chunk-4P355ZWB.js";
36
38
 
37
39
  // src/commands/capability/index.ts
38
40
  import * as readline from "readline";
@@ -178,15 +180,16 @@ function registerCapability(program) {
178
180
  await executeAndPrint(program, async (host) => {
179
181
  const gatewayDomain = resolveCapabilityListDomain(opts.domain);
180
182
  const projectId = parseOptionalProjectId(opts.projectId);
181
- const catalog = normalizeCapabilityList(await listCapabilities(host, gatewayDomain, projectId));
183
+ const envelope = await listCapabilitiesWithEnvelope(host, gatewayDomain, projectId);
184
+ const catalog = normalizeCapabilityList(envelope.data);
182
185
  const capabilities = filterCapabilities(catalog, opts.domain);
183
186
  const warning = emptyCapabilityCatalogWarning(catalog);
184
- return {
187
+ return withOutputMetadata({
185
188
  domain: opts.domain,
186
189
  count: capabilities.length,
187
190
  capabilities,
188
191
  ...warning ? { warning } : {}
189
- };
192
+ }, envelope.meta);
190
193
  });
191
194
  }).addHelpText(
192
195
  "after",
@@ -196,16 +199,17 @@ function registerCapability(program) {
196
199
  await executeAndPrint(program, async (host) => {
197
200
  const gatewayDomain = resolveCapabilityListDomain(opts.domain);
198
201
  const projectId = parseOptionalProjectId(opts.projectId);
199
- const catalog = normalizeCapabilityList(await listCapabilities(host, gatewayDomain, projectId));
202
+ const envelope = await listCapabilitiesWithEnvelope(host, gatewayDomain, projectId);
203
+ const catalog = normalizeCapabilityList(envelope.data);
200
204
  const capabilities = filterCapabilities(catalog, opts.domain, query);
201
205
  const warning = emptyCapabilityCatalogWarning(catalog);
202
- return {
206
+ return withOutputMetadata({
203
207
  domain: opts.domain,
204
208
  query,
205
209
  count: capabilities.length,
206
210
  capabilities,
207
211
  ...warning ? { warning } : {}
208
- };
212
+ }, envelope.meta);
209
213
  });
210
214
  }).addHelpText(
211
215
  "after",
@@ -214,7 +218,7 @@ function registerCapability(program) {
214
218
  capability.command("inspect").description("Inspect one capability schema, risk, auth, and output metadata").argument("<capability-id>", "Capability ID, such as analysis.report.list").option("--domain <domain>", "Override the capability namespace used for gateway routing").option("--project-id <project-id>", "Check availability in a project before returning metadata").action(async (capabilityId, opts) => {
215
219
  await executeAndPrint(program, async (host) => {
216
220
  const gatewayDomain = resolveCapabilityGatewayDomain(capabilityId, opts.domain);
217
- return inspectCapability(host, gatewayDomain, capabilityId, parseOptionalProjectId(opts.projectId));
221
+ return gatewayOutput(await inspectCapabilityWithEnvelope(host, gatewayDomain, capabilityId, parseOptionalProjectId(opts.projectId)), host);
218
222
  });
219
223
  }).addHelpText(
220
224
  "after",
@@ -225,7 +229,7 @@ function registerCapability(program) {
225
229
  ).argument("<capability-id>", "Capability ID, such as metadata.data_table.sql_write").option("--domain <domain>", "Override the capability namespace used for gateway routing").option("--input <json-or-path>", "Input JSON object, file path, @<path>, or - for stdin").action(async (capabilityId, opts) => {
226
230
  await executeAndPrint(program, async (host) => {
227
231
  const gatewayDomain = resolveCapabilityGatewayDomain(capabilityId, opts.domain);
228
- return validateCapability(host, gatewayDomain, capabilityId, parseCapabilityInput(opts.input));
232
+ return gatewayOutput(await validateCapabilityWithEnvelope(host, gatewayDomain, capabilityId, parseCapabilityInput(opts.input)), host);
229
233
  });
230
234
  }).addHelpText(
231
235
  "after",
@@ -247,7 +251,7 @@ Examples:
247
251
  ).argument("<capability-id>", "Capability ID, such as analysis.query.export").option("--domain <domain>", "Override the capability namespace used for gateway routing").option("--input <json-or-path>", "Input JSON object, file path, @<path>, or - for stdin").action(async (capabilityId, opts) => {
248
252
  await executeAndPrint(program, async (host) => {
249
253
  const gatewayDomain = resolveCapabilityGatewayDomain(capabilityId, opts.domain);
250
- return dryRunCapability(host, gatewayDomain, capabilityId, parseCapabilityInput(opts.input));
254
+ return gatewayOutput(await dryRunCapabilityWithEnvelope(host, gatewayDomain, capabilityId, parseCapabilityInput(opts.input)), host);
251
255
  });
252
256
  }).addHelpText(
253
257
  "after",
@@ -267,10 +271,10 @@ Examples:
267
271
  );
268
272
  }
269
273
  if (globalOpts.validate) {
270
- return validateCapability(host, gatewayDomain, capabilityId, input);
274
+ return gatewayOutput(await validateCapabilityWithEnvelope(host, gatewayDomain, capabilityId, input), host);
271
275
  }
272
276
  if (globalOpts.dryRun) {
273
- return dryRunCapability(host, gatewayDomain, capabilityId, input);
277
+ return gatewayOutput(await dryRunCapabilityWithEnvelope(host, gatewayDomain, capabilityId, input), host);
274
278
  }
275
279
  if (!globalOpts.yes) {
276
280
  const metadata = await inspectCapability(host, gatewayDomain, capabilityId);
@@ -283,7 +287,7 @@ Examples:
283
287
  }
284
288
  }
285
289
  }
286
- return executeCapability(host, gatewayDomain, capabilityId, input);
290
+ return gatewayOutput(await executeCapabilityWithEnvelope(host, gatewayDomain, capabilityId, input), host);
287
291
  });
288
292
  }).addHelpText(
289
293
  "after",
@@ -293,6 +297,15 @@ Examples:
293
297
  ae-cli capability run analysis.dashboard.list --input input.json`
294
298
  );
295
299
  }
300
+ function gatewayOutput(envelope, host) {
301
+ return withOutputMetadata(withPageUrl(envelope.data, host), envelope.meta);
302
+ }
303
+ function withPageUrl(value, host) {
304
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
305
+ const record = value;
306
+ if (typeof record.page_path !== "string" || !/^\/(?![\\/])/.test(record.page_path) || record.page_url !== void 0) return value;
307
+ return { ...record, page_url: new URL(record.page_path, host).toString() };
308
+ }
296
309
  async function executeAndPrint(program, action) {
297
310
  const globalOpts = program.opts();
298
311
  const host = resolveHost(globalOpts.host);
@@ -312,11 +325,11 @@ async function executeAndPrint(program, action) {
312
325
  process.exitCode = 1;
313
326
  return;
314
327
  }
315
- printCapabilityError(error);
328
+ printCapabilityError(error, host);
316
329
  process.exitCode = 1;
317
330
  }
318
331
  }
319
- function printCapabilityError(error) {
332
+ function printCapabilityError(error, host) {
320
333
  if (error instanceof CapabilityCommandValidationError) {
321
334
  printError("validation", error.message, error.hint, error.code);
322
335
  return;
@@ -330,7 +343,7 @@ function printCapabilityError(error) {
330
343
  return;
331
344
  }
332
345
  if (error instanceof CapabilityGatewayError) {
333
- printError("api", error.message, error.hint, error.code);
346
+ printError("api", error.message, error.hint, error.code, withPageUrl(error.meta, host));
334
347
  return;
335
348
  }
336
349
  const message = error instanceof Error ? error.message : String(error);