@thinkingai/ae-cli 6.1.19 → 6.1.20

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 (82) hide show
  1. package/README.md +95 -38
  2. package/README.zh.md +95 -38
  3. package/dist/{auth-QPEVVBKT.js → auth-FUM37MHF.js} +236 -127
  4. package/dist/{capability-ASI62I4A.js → capability-AXFQW5WM.js} +45 -32
  5. package/dist/{chunk-FR2EDAQM.js → chunk-4P355ZWB.js} +69 -1
  6. package/dist/chunk-6ZIQV5GW.js +26 -0
  7. package/dist/chunk-ATSM5XAW.js +623 -0
  8. package/dist/{chunk-6QK7SYYY.js → chunk-BBEFP4SB.js} +29 -37
  9. package/dist/{chunk-KEWVSO64.js → chunk-CZU3V3DQ.js} +4 -15
  10. package/dist/chunk-ECBLHAZO.js +15 -0
  11. package/dist/{chunk-B5HMQAOM.js → chunk-I4WQAEYB.js} +1 -1
  12. package/dist/{chunk-QL6R6TSK.js → chunk-LNZBEQXW.js} +6 -5
  13. package/dist/{chunk-GJXRF7Y6.js → chunk-QJQZH5GQ.js} +47 -78
  14. package/dist/{chunk-S5PONFBM.js → chunk-RSG4ONOI.js} +7 -7
  15. package/dist/{chunk-DKEXH6Q5.js → chunk-TAL6CZH6.js} +6 -6
  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-62EWBEIG.js → metadata-VZVC7YMH.js} +7 -9
  25. package/dist/{model-BHLJ2QMT.js → model-E4JMQ4V2.js} +4 -6
  26. package/dist/{personal-semantic-preference-JBVMAI7L.js → personal-semantic-preference-7S2SQ3UV.js} +6 -8
  27. package/dist/{project-semantic-BQ4MPUS5.js → project-semantic-2SZP2OPO.js} +11 -13
  28. package/dist/{sync-MUWPRM5I.js → sync-YV3E66IF.js} +6 -8
  29. package/dist/{te-agent-WQZTMEK4.js → te-agent-JB5T3PO7.js} +393 -90
  30. package/dist/{te-analysis-MNCG65LU.js → te-analysis-3YJAAT2D.js} +99 -33
  31. package/dist/{te-community-BNE6KREL.js → te-community-UDBI672N.js} +9 -32
  32. package/dist/{te-dataops-TXLKM2W6.js → te-dataops-ZLYOCXZ4.js} +19 -20
  33. package/dist/{te-engage-TPKLRVTL.js → te-engage-4XG6OJML.js} +12 -14
  34. package/dist/{te-experiment-6BYKTCY7.js → te-experiment-VXUWPINJ.js} +6 -8
  35. package/dist/{te-kb-BUD6N3NI.js → te-kb-WYQWHFSC.js} +595 -85
  36. package/dist/{te-system-44LZJDTQ.js → te-system-7G6F2LJA.js} +6 -8
  37. package/dist/{te-team-XMP47YXE.js → te-team-E7FBBXMQ.js} +6 -8
  38. package/dist/{update-UODFOZ4P.js → update-D47BUG25.js} +7 -8
  39. package/package.json +12 -9
  40. package/skills/ae-agent/SKILL.md +30 -13
  41. package/skills/ae-agent/references/agent-distribution.md +94 -0
  42. package/skills/ae-agent/references/approval-request.md +4 -0
  43. package/skills/ae-agent/references/command_index.md +9 -2
  44. package/skills/ae-agent/references/create-automation.md +20 -2
  45. package/skills/ae-agent/references/get-agent-context.md +70 -0
  46. package/skills/ae-agent/references/list-automations.md +18 -3
  47. package/skills/ae-agent/references/list-mcp-credentials.md +1 -1
  48. package/skills/ae-agent/references/mcp-token.md +3 -3
  49. package/skills/ae-agent/references/set-mcp-credential.md +0 -1
  50. package/skills/ae-agent/references/update-automation.md +18 -0
  51. package/skills/ae-analysis/SKILL.md +11 -2
  52. package/skills/ae-analysis/references/adhoc_run.md +2 -0
  53. package/skills/ae-analysis/references/ai_models.md +23 -3
  54. package/skills/ae-analysis/references/analysis_gateway_assets.md +2 -2
  55. package/skills/ae-analysis/references/command_index.md +7 -7
  56. package/skills/ae-analysis/references/cross_source_config.md +84 -0
  57. package/skills/ae-analysis/references/dashboard_update.md +1 -1
  58. package/skills/ae-analysis/references/user_tag_create.md +23 -1
  59. package/skills/ae-analysis/references/user_tag_models.md +9 -1
  60. package/skills/ae-analysis/references/user_tag_update.md +14 -2
  61. package/skills/ae-data-integration/SKILL.md +2 -1
  62. package/skills/ae-data-integration/references/dimension-routing.md +36 -0
  63. package/skills/ae-data-integration/references/local-analysis.md +2 -0
  64. package/skills/ae-data-integration/references/source-inspect.md +1 -1
  65. package/skills/ae-data-integration/references/tracking-plan.md +1 -1
  66. package/skills/ae-data-integration/references/transform.md +1 -1
  67. package/skills/ae-data-integration/references/ue-mapping.md +5 -2
  68. package/skills/ae-data-integration/references/ue-routing.md +17 -1
  69. package/skills/ae-generate-tracking-plan/SKILL.md +25 -13
  70. package/skills/ae-generate-tracking-plan/references/business-dimension-mapping.md +1 -1
  71. package/skills/ae-kb/SKILL.md +107 -36
  72. package/skills/ae-kb/references/query-workflow.md +59 -29
  73. package/skills/ae-kb/references/versions.md +46 -0
  74. package/skills/ae-system/SKILL.md +1 -1
  75. package/skills/ae-use-agent/SKILL.md +42 -0
  76. package/skills/ae-use-agent/references/local-agent.md +114 -0
  77. package/dist/auth-UMJAA72O.js +0 -15
  78. package/dist/chunk-533JK3YX.js +0 -72
  79. package/dist/chunk-O6K2ODUE.js +0 -318
  80. package/dist/chunk-RWXIXOZG.js +0 -326
  81. package/dist/client-N7NGZUNI.js +0 -204
  82. 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
+ CapabilityGatewayError,
8
+ dryRunCapabilityWithEnvelope,
9
+ executeCapabilityWithEnvelope,
10
+ inspectCapability,
11
+ inspectCapabilityWithEnvelope,
12
+ listCapabilitiesWithEnvelope,
13
+ validateCapabilityWithEnvelope
14
+ } from "./chunk-QJQZH5GQ.js";
6
15
  import {
7
16
  findGatewayDomain,
8
17
  listRegisteredCapabilityDomains,
9
18
  resolveGatewayDomain
10
19
  } from "./chunk-TKHSULJT.js";
11
20
  import {
12
- printError,
13
- printOutput
14
- } from "./chunk-KEWVSO64.js";
21
+ resolveHost
22
+ } from "./chunk-ECBLHAZO.js";
15
23
  import {
16
- CapabilityGatewayError,
17
- dryRunCapability,
18
- executeCapability,
19
- inspectCapability,
20
- listCapabilities,
21
- validateCapability
22
- } from "./chunk-GJXRF7Y6.js";
24
+ printError,
25
+ printOutput,
26
+ withOutputMetadata
27
+ } from "./chunk-CZU3V3DQ.js";
23
28
  import "./chunk-Y74WTIKJ.js";
24
- import "./chunk-RWXIXOZG.js";
25
29
  import {
26
- SecureStoreAuthError,
27
- resolveHost
28
- } from "./chunk-O6K2ODUE.js";
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",
@@ -199,16 +202,17 @@ function registerCapability(program) {
199
202
  await executeAndPrint(program, async (host) => {
200
203
  const gatewayDomain = resolveCapabilityListDomain(opts.domain);
201
204
  const projectId = parseOptionalProjectId(opts.projectId);
202
- const catalog = normalizeCapabilityList(await listCapabilities(host, gatewayDomain, projectId));
205
+ const envelope = await listCapabilitiesWithEnvelope(host, gatewayDomain, projectId);
206
+ const catalog = normalizeCapabilityList(envelope.data);
203
207
  const capabilities = filterCapabilities(catalog, opts.domain, query);
204
208
  const warning = emptyCapabilityCatalogWarning(catalog);
205
- return {
209
+ return withOutputMetadata({
206
210
  domain: opts.domain,
207
211
  query,
208
212
  count: capabilities.length,
209
213
  capabilities,
210
214
  ...warning ? { warning } : {}
211
- };
215
+ }, envelope.meta);
212
216
  });
213
217
  }).addHelpText(
214
218
  "after",
@@ -217,7 +221,7 @@ function registerCapability(program) {
217
221
  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) => {
218
222
  await executeAndPrint(program, async (host) => {
219
223
  const gatewayDomain = resolveCapabilityGatewayDomain(capabilityId, opts.domain);
220
- return inspectCapability(host, gatewayDomain, capabilityId, parseOptionalProjectId(opts.projectId));
224
+ return gatewayOutput(await inspectCapabilityWithEnvelope(host, gatewayDomain, capabilityId, parseOptionalProjectId(opts.projectId)), host);
221
225
  });
222
226
  }).addHelpText(
223
227
  "after",
@@ -228,7 +232,7 @@ function registerCapability(program) {
228
232
  ).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) => {
229
233
  await executeAndPrint(program, async (host) => {
230
234
  const gatewayDomain = resolveCapabilityGatewayDomain(capabilityId, opts.domain);
231
- return validateCapability(host, gatewayDomain, capabilityId, parseCapabilityInput(opts.input));
235
+ return gatewayOutput(await validateCapabilityWithEnvelope(host, gatewayDomain, capabilityId, parseCapabilityInput(opts.input)), host);
232
236
  });
233
237
  }).addHelpText(
234
238
  "after",
@@ -250,7 +254,7 @@ Examples:
250
254
  ).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) => {
251
255
  await executeAndPrint(program, async (host) => {
252
256
  const gatewayDomain = resolveCapabilityGatewayDomain(capabilityId, opts.domain);
253
- return dryRunCapability(host, gatewayDomain, capabilityId, parseCapabilityInput(opts.input));
257
+ return gatewayOutput(await dryRunCapabilityWithEnvelope(host, gatewayDomain, capabilityId, parseCapabilityInput(opts.input)), host);
254
258
  });
255
259
  }).addHelpText(
256
260
  "after",
@@ -270,10 +274,10 @@ Examples:
270
274
  );
271
275
  }
272
276
  if (globalOpts.validate) {
273
- return validateCapability(host, gatewayDomain, capabilityId, input);
277
+ return gatewayOutput(await validateCapabilityWithEnvelope(host, gatewayDomain, capabilityId, input), host);
274
278
  }
275
279
  if (globalOpts.dryRun) {
276
- return dryRunCapability(host, gatewayDomain, capabilityId, input);
280
+ return gatewayOutput(await dryRunCapabilityWithEnvelope(host, gatewayDomain, capabilityId, input), host);
277
281
  }
278
282
  if (!globalOpts.yes) {
279
283
  const metadata = await inspectCapability(host, gatewayDomain, capabilityId);
@@ -286,7 +290,7 @@ Examples:
286
290
  }
287
291
  }
288
292
  }
289
- return executeCapability(host, gatewayDomain, capabilityId, input);
293
+ return gatewayOutput(await executeCapabilityWithEnvelope(host, gatewayDomain, capabilityId, input), host);
290
294
  });
291
295
  }).addHelpText(
292
296
  "after",
@@ -296,6 +300,15 @@ Examples:
296
300
  ae-cli capability run analysis.dashboard.list --input input.json`
297
301
  );
298
302
  }
303
+ function gatewayOutput(envelope, host) {
304
+ return withOutputMetadata(withPageUrl(envelope.data, host), envelope.meta);
305
+ }
306
+ function withPageUrl(value, host) {
307
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
308
+ const record = value;
309
+ if (typeof record.page_path !== "string" || !/^\/(?![\\/])/.test(record.page_path) || record.page_url !== void 0) return value;
310
+ return { ...record, page_url: new URL(record.page_path, host).toString() };
311
+ }
299
312
  async function executeAndPrint(program, action) {
300
313
  const globalOpts = program.opts();
301
314
  const host = resolveHost(globalOpts.host);
@@ -315,11 +328,11 @@ async function executeAndPrint(program, action) {
315
328
  process.exitCode = 1;
316
329
  return;
317
330
  }
318
- printCapabilityError(error);
331
+ printCapabilityError(error, host);
319
332
  process.exitCode = 1;
320
333
  }
321
334
  }
322
- function printCapabilityError(error) {
335
+ function printCapabilityError(error, host) {
323
336
  if (error instanceof CapabilityCommandValidationError) {
324
337
  printError("validation", error.message, error.hint, error.code);
325
338
  return;
@@ -333,7 +346,7 @@ function printCapabilityError(error) {
333
346
  return;
334
347
  }
335
348
  if (error instanceof CapabilityGatewayError) {
336
- printError("api", error.message, error.hint, error.code);
349
+ printError("api", error.message, error.hint, error.code, withPageUrl(error.meta, host));
337
350
  return;
338
351
  }
339
352
  const message = error instanceof Error ? error.message : String(error);