@thinkingai/ae-cli 6.0.48-customer.1 → 6.1.0-project-role-upsert.1

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 (144) hide show
  1. package/README.md +96 -36
  2. package/README.zh.md +96 -36
  3. package/dist/{auth-QPEVVBKT.js → auth-FUM37MHF.js} +236 -127
  4. package/dist/{capability-RUSV77CO.js → capability-AXFQW5WM.js} +49 -33
  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-NSIVXKJH.js → chunk-JDPEVVHR.js} +5 -4
  13. package/dist/{chunk-BE73PUFX.js → chunk-LNZBEQXW.js} +2 -2
  14. package/dist/{chunk-GJXRF7Y6.js → chunk-QJQZH5GQ.js} +47 -78
  15. package/dist/{chunk-BVDST5YT.js → chunk-RSG4ONOI.js} +7 -7
  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-FZQQC3KW.js → data-integration-4NZ53OMT.js} +7 -9
  21. package/dist/index.js +61 -78
  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-SOALPEA7.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-LOF33N5X.js} +6 -8
  27. package/dist/project-semantic-RV6NA5LN.js +1113 -0
  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-YWCSSLYL.js → te-analysis-TV2YZ4IC.js} +112 -30
  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-VEW4CJHK.js → te-engage-EUVMTUDJ.js} +67 -11
  34. package/dist/te-experiment-ESEYE2SK.js +1059 -0
  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 +13 -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 +28 -3
  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 +8 -8
  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/personal_semantic_preference_add.md +1 -1
  59. package/skills/ae-analysis/references/personal_semantic_preference_get.md +3 -1
  60. package/skills/ae-analysis/references/personal_semantic_preference_list.md +3 -1
  61. package/skills/ae-analysis/references/personal_semantic_preference_update.md +1 -1
  62. package/skills/ae-analysis/references/project_role_upsert.md +32 -2
  63. package/skills/ae-analysis/references/user_tag_create.md +23 -1
  64. package/skills/ae-analysis/references/user_tag_models.md +9 -1
  65. package/skills/ae-analysis/references/user_tag_update.md +14 -2
  66. package/skills/ae-data-integration/SKILL.md +2 -1
  67. package/skills/ae-data-integration/references/dimension-routing.md +36 -0
  68. package/skills/ae-data-integration/references/local-analysis.md +2 -0
  69. package/skills/ae-data-integration/references/source-inspect.md +1 -1
  70. package/skills/ae-data-integration/references/ue-routing.md +17 -1
  71. package/skills/ae-engage/SKILL.md +2 -0
  72. package/skills/ae-engage/references/save-flow.md +37 -1
  73. package/skills/ae-experiment/SKILL.md +117 -0
  74. package/skills/ae-experiment/references/batch_delete_experiment.md +11 -0
  75. package/skills/ae-experiment/references/batch_delete_feature.md +11 -0
  76. package/skills/ae-experiment/references/batch_delete_traffic_layer.md +11 -0
  77. package/skills/ae-experiment/references/cancel_experiment_query_by_request_id.md +11 -0
  78. package/skills/ae-experiment/references/check_experiment_ready.md +17 -0
  79. package/skills/ae-experiment/references/check_experiment_traffic_layer_conflict.md +37 -0
  80. package/skills/ae-experiment/references/delete_metric.md +13 -0
  81. package/skills/ae-experiment/references/manage_experiment.md +17 -0
  82. package/skills/ae-experiment/references/manage_feature_status.md +11 -0
  83. package/skills/ae-experiment/references/manage_feature_whitelist.md +66 -0
  84. package/skills/ae-experiment/references/manage_guardrail_metrics.md +26 -0
  85. package/skills/ae-experiment/references/query_bucket_list.md +12 -0
  86. package/skills/ae-experiment/references/query_experiment_detail.md +19 -0
  87. package/skills/ae-experiment/references/query_experiment_list.md +22 -0
  88. package/skills/ae-experiment/references/query_experiment_list_archived.md +18 -0
  89. package/skills/ae-experiment/references/query_experiment_metric_trend.md +12 -0
  90. package/skills/ae-experiment/references/query_experiment_operation_log.md +17 -0
  91. package/skills/ae-experiment/references/query_experiment_report_summary.md +12 -0
  92. package/skills/ae-experiment/references/query_experiment_sample_size_report.md +13 -0
  93. package/skills/ae-experiment/references/query_feature_detail.md +14 -0
  94. package/skills/ae-experiment/references/query_feature_list.md +12 -0
  95. package/skills/ae-experiment/references/query_feature_operation_log.md +19 -0
  96. package/skills/ae-experiment/references/query_feature_version_list.md +20 -0
  97. package/skills/ae-experiment/references/query_metric_detail.md +18 -0
  98. package/skills/ae-experiment/references/query_metric_list.md +15 -0
  99. package/skills/ae-experiment/references/query_traffic_layer_detail.md +13 -0
  100. package/skills/ae-experiment/references/query_traffic_layer_list.md +12 -0
  101. package/skills/ae-experiment/references/save_build_guide.md +39 -0
  102. package/skills/ae-experiment/references/save_experiment.md +210 -0
  103. package/skills/ae-experiment/references/save_feature.md +13 -0
  104. package/skills/ae-experiment/references/save_metric.md +91 -0
  105. package/skills/ae-experiment/references/save_submit_experiment.md +16 -0
  106. package/skills/ae-experiment/references/save_traffic_layer.md +13 -0
  107. package/skills/ae-experiment/references/save_validate.md +33 -0
  108. package/skills/ae-experiment/references/update_experiment_group.md +20 -0
  109. package/skills/ae-experiment-design/SKILL.md +149 -0
  110. package/skills/ae-experiment-design/agents/openai.yaml +4 -0
  111. package/skills/ae-experiment-design/references/client-experiment-sdk.md +147 -0
  112. package/skills/ae-experiment-design/references/experiment-creation.md +108 -0
  113. package/skills/ae-experiment-design/references/experiment-sdk-contract.md +100 -0
  114. package/skills/ae-experiment-design/references/exposure-contract.md +91 -0
  115. package/skills/ae-experiment-design/references/hybrid-experiment-sdk.md +74 -0
  116. package/skills/ae-experiment-design/references/metric-readiness.md +143 -0
  117. package/skills/ae-experiment-design/references/platform-operations.md +105 -0
  118. package/skills/ae-experiment-design/references/sdk-index.md +76 -0
  119. package/skills/ae-experiment-design/references/sdk-integration.md +114 -0
  120. package/skills/ae-experiment-design/references/sdk-troubleshooting.md +139 -0
  121. package/skills/ae-experiment-design/references/server-experiment-sdk.md +78 -0
  122. package/skills/ae-experiment-design/scripts/calculate_experiment_plan.py +450 -0
  123. package/skills/ae-experiment-insight/SKILL.md +149 -0
  124. package/skills/ae-experiment-insight/agents/openai.yaml +4 -0
  125. package/skills/ae-experiment-insight/references/decision-framework.md +69 -0
  126. package/skills/ae-experiment-insight/references/diagnostic-playbook.md +225 -0
  127. package/skills/ae-experiment-insight/references/platform-operations.md +82 -0
  128. package/skills/ae-experiment-insight/scripts/analyze_experiment.py +478 -0
  129. package/skills/ae-generate-tracking-plan/SKILL.md +16 -5
  130. package/skills/ae-kb/SKILL.md +107 -36
  131. package/skills/ae-kb/references/query-workflow.md +59 -29
  132. package/skills/ae-kb/references/versions.md +46 -0
  133. package/skills/ae-project-semantic/SKILL.md +193 -0
  134. package/skills/ae-project-semantic/references/query-routing-v5.md +165 -0
  135. package/skills/ae-project-semantic/references/recommendation-quality.md +68 -0
  136. package/skills/ae-system/SKILL.md +1 -1
  137. package/skills/ae-use-agent/SKILL.md +42 -0
  138. package/skills/ae-use-agent/references/local-agent.md +114 -0
  139. package/dist/auth-UMJAA72O.js +0 -15
  140. package/dist/chunk-533JK3YX.js +0 -72
  141. package/dist/chunk-O6K2ODUE.js +0 -318
  142. package/dist/chunk-RWXIXOZG.js +0 -326
  143. package/dist/client-N7NGZUNI.js +0 -204
  144. 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
  };