@luckypiggy/zhihu-ai-cli 0.1.0-test.1 → 0.2.1-test.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2,7 +2,8 @@ import { existsSync, lstatSync } from "node:fs";
2
2
  import { isIP } from "node:net";
3
3
  import { isAbsolute, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { CLI_VERSION, MIN_NODE_MAJOR, } from "./contracts.js";
5
+ import { CLI_VERSION, CLI_PACKAGE_NAME, MIN_NODE_MAJOR, } from "./contracts.js";
6
+ import { CLI_DOCTOR_COMMAND, CLI_RUNNER } from "./release.js";
6
7
  import { ConfigError, DEFAULT_PROFILE, DEFAULT_PROFILE_NAME, checkProfile, configRoot, guardProfileConnectionChange, initializeProfile, ProfileConnectionGuardError, readConfigSnapshot, readProfileRaw, validateProfile, } from "./config.js";
7
8
  import { withProfileLock } from "./profile-lock.js";
8
9
  import { emitError, emitSuccess } from "./output.js";
@@ -13,6 +14,7 @@ import { IntegrationHttpClient, HttpError, validateIntegrationInfo, } from "./ht
13
14
  import { CliAuthError, TokenManager, mapFailure } from "./token-manager.js";
14
15
  import { CodexSkillError, inspectCodexSkill, installCodexSkill, uninstallCodexSkill, } from "./codex-skill.js";
15
16
  import { InstallError, installWizard } from "./install.js";
17
+ import { promptTerminalCredentials, TerminalPromptCancelledError } from "./terminal-prompt.js";
16
18
  class CliFailure extends Error {
17
19
  shape;
18
20
  constructor(shape) {
@@ -120,7 +122,7 @@ function versionData() {
120
122
  cli_version: CLI_VERSION,
121
123
  node_version: process.versions.node,
122
124
  node_supported: major >= MIN_NODE_MAJOR,
123
- package: "@luckypiggy/zhihu-ai-cli",
125
+ package: CLI_PACKAGE_NAME,
124
126
  stage: "p1-readonly",
125
127
  };
126
128
  }
@@ -208,16 +210,20 @@ export async function doctorData(environment = process.env, options = {}) {
208
210
  try {
209
211
  client = options.clientFactory ? options.clientFactory(profile) : new IntegrationHttpClient(profile);
210
212
  const info = validateIntegrationInfo(await client.info(), false);
211
- const ready = info.status === "ready" && info.auth?.configured === true;
213
+ const legacyAuth = info.auth?.mode === "pilot_oauth_bff";
214
+ const ready = info.status === "ready"
215
+ && info.auth?.configured === true
216
+ && info.auth?.mode === "pilot_terminal_password";
212
217
  serviceValidatedReady = ready;
213
218
  service = {
214
219
  ok: ready,
215
- state: ready ? "ready" : "configuration_required",
220
+ state: ready ? "ready" : legacyAuth ? "terminal_login_required" : "configuration_required",
216
221
  service: info.service,
217
222
  protocol_version: info.protocol_version,
218
223
  minimum_cli_version: info.minimum_cli_version,
219
224
  compatible: true,
220
225
  status: info.status,
226
+ ...(legacyAuth ? { detail: "This Pilot exposes legacy browser OAuth; terminal login is required for this CLI" } : {}),
221
227
  capabilities: info.capabilities,
222
228
  };
223
229
  network = { ok: true, state: "reachable" };
@@ -326,21 +332,50 @@ function authService(flags) {
326
332
  throw mapFailure(error);
327
333
  }
328
334
  const credentials = new SecureCredentialStore(selected.name);
329
- return new AuthService(client, credentials, selected.name, {
330
- onBrowserFailure: (authorizationUrl) => {
331
- process.stderr.write(`Browser launch failed. Open this URL manually: ${authorizationUrl}\n`);
332
- },
333
- });
335
+ return { service: new AuthService(client, credentials, selected.name), profile: selected.profile, profileName: selected.name };
334
336
  }
335
- async function authCommand(action, flags) {
337
+ async function authCommand(action, flags, json = false) {
336
338
  if (action !== "login" && action !== "status" && action !== "logout")
337
339
  invalid("Use auth login, auth status, or auth logout");
338
- const service = authService(flags);
339
- if (action === "login")
340
- return service.login();
340
+ for (const key of flags.keys())
341
+ if (key !== "profile")
342
+ invalid(`Unknown auth option: --${key}`);
343
+ const selected = authService(flags);
344
+ if (action === "login") {
345
+ if (json || !(process.stdin.isTTY
346
+ && process.stderr.isTTY)) {
347
+ throw new CliFailure({ code: "INTERACTIVE_REQUIRED", message: "Interactive terminal input is required for login; do not pass credentials through flags or environment variables", retryable: false, exit_code: 2 });
348
+ }
349
+ const controller = new AbortController();
350
+ const onSignal = () => controller.abort();
351
+ process.once("SIGINT", onSignal);
352
+ process.once("SIGTERM", onSignal);
353
+ try {
354
+ const credentials = await promptTerminalCredentials({
355
+ usernameLabel: selected.profile.language === "zh" ? "智护用户名" : "Zhihu username",
356
+ tenantLabel: selected.profile.language === "zh" ? "租户 ID(可留空)" : "Tenant id (optional)",
357
+ captchaLabel: selected.profile.language === "zh" ? "验证码(可留空)" : "Captcha verification (optional)",
358
+ passwordLabel: selected.profile.language === "zh" ? "智护密码:" : "Zhihu password: ",
359
+ defaultUsername: selected.profile.username,
360
+ defaultTenantId: selected.profile.tenant_id,
361
+ signal: controller.signal,
362
+ });
363
+ return await selected.service.login(credentials, { signal: controller.signal });
364
+ }
365
+ catch (error) {
366
+ if (error instanceof TerminalPromptCancelledError) {
367
+ throw new CliFailure({ code: "USER_CANCELLED", message: "Login cancelled", retryable: false, exit_code: 2 });
368
+ }
369
+ throw error;
370
+ }
371
+ finally {
372
+ process.removeListener("SIGINT", onSignal);
373
+ process.removeListener("SIGTERM", onSignal);
374
+ }
375
+ }
341
376
  if (action === "status")
342
- return service.status();
343
- return service.logout();
377
+ return selected.service.status();
378
+ return selected.service.logout();
344
379
  }
345
380
  async function capabilitiesCommand(action, flags) {
346
381
  if (action !== "list")
@@ -357,6 +392,108 @@ async function capabilitiesCommand(action, flags) {
357
392
  const tokenManager = new TokenManager(client, credentials, selected.name);
358
393
  return tokenManager.callReadOnly((accessToken) => client.capabilities(accessToken));
359
394
  }
395
+ const A2A_CAPABILITIES = new Set([
396
+ "resource_query",
397
+ "incident_analysis",
398
+ "inspection_summary",
399
+ "knowledge_qa",
400
+ ]);
401
+ function a2aTextOption(flags, key, maxChars, maxBytes) {
402
+ const value = flags.get(key);
403
+ if (typeof value !== "string")
404
+ invalid(`--${key} requires a value`);
405
+ const normalized = value.trim();
406
+ if (!normalized || Array.from(value).length > maxChars || Buffer.byteLength(value, "utf8") > maxBytes
407
+ || /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(value)) {
408
+ invalid(`--${key} must be non-empty text up to ${maxChars} characters and ${maxBytes} UTF-8 bytes`);
409
+ }
410
+ return normalized;
411
+ }
412
+ function a2aTaskIdOption(flags) {
413
+ const value = flags.get("id");
414
+ if (typeof value !== "string" || !/^[A-Za-z0-9:_-]{1,128}$/.test(value)) {
415
+ invalid("--id must be an opaque A2A task id");
416
+ }
417
+ return value;
418
+ }
419
+ function a2aHumanSummary(data, language) {
420
+ if (!data || typeof data !== "object" || Array.isArray(data))
421
+ return undefined;
422
+ const value = data;
423
+ if (typeof value.task !== "string" || typeof value.target !== "string" || typeof value.result !== "string")
424
+ return undefined;
425
+ if (language === "zh") {
426
+ return [`目标员工:${value.target}`, `任务:${value.task}`, `结果:${value.result}`].join("\n");
427
+ }
428
+ return [`Target employee: ${value.target}`, `Task: ${value.task}`, `Result: ${value.result}`].join("\n");
429
+ }
430
+ async function a2aCommand(command, flags) {
431
+ const [_, action, subaction] = command;
432
+ const allowed = new Set(["profile", "capability", "message", "id"]);
433
+ for (const key of flags.keys())
434
+ if (!allowed.has(key))
435
+ invalid(`Unknown a2a option: --${key}`);
436
+ let operation;
437
+ let capability;
438
+ let message;
439
+ let taskId;
440
+ if (action === "capabilities" && command.length === 2) {
441
+ if (["capability", "message", "id"].some((key) => flags.has(key))) {
442
+ invalid("a2a capabilities does not accept --capability, --message, or --id");
443
+ }
444
+ operation = "capabilities";
445
+ }
446
+ else if (action === "delegate" && command.length === 2) {
447
+ if (flags.has("id"))
448
+ invalid("a2a delegate does not accept --id");
449
+ const rawCapability = a2aTextOption(flags, "capability", 128, 128);
450
+ if (!A2A_CAPABILITIES.has(rawCapability)) {
451
+ invalid("--capability must be resource_query, incident_analysis, inspection_summary, or knowledge_qa");
452
+ }
453
+ capability = rawCapability;
454
+ message = a2aTextOption(flags, "message", 8000, 16 * 1024);
455
+ operation = "delegate";
456
+ }
457
+ else if (action === "task" && subaction === "get" && command.length === 3) {
458
+ if (flags.has("capability") || flags.has("message"))
459
+ invalid("a2a task get does not accept --capability or --message");
460
+ taskId = a2aTaskIdOption(flags);
461
+ operation = "task";
462
+ }
463
+ else {
464
+ invalid("Use a2a capabilities, a2a delegate --capability <id> --message <text>, or a2a task get --id <id>");
465
+ }
466
+ const selected = selectedProfile(flags);
467
+ let client;
468
+ try {
469
+ client = new IntegrationHttpClient(selected.profile);
470
+ }
471
+ catch (error) {
472
+ throw mapFailure(error);
473
+ }
474
+ const credentials = new SecureCredentialStore(selected.name);
475
+ const tokenManager = new TokenManager(client, credentials, selected.name);
476
+ const controller = new AbortController();
477
+ const onSignal = () => controller.abort();
478
+ process.once("SIGINT", onSignal);
479
+ process.once("SIGTERM", onSignal);
480
+ try {
481
+ if (operation === "capabilities") {
482
+ const data = await tokenManager.callReadOnly((accessToken) => client.a2aCapabilities(accessToken, { signal: controller.signal }));
483
+ return { data };
484
+ }
485
+ if (operation === "delegate") {
486
+ const data = await tokenManager.callReadOnly((accessToken) => client.a2aDelegate(capability, message, accessToken, { signal: controller.signal }));
487
+ return { data, human: a2aHumanSummary(data, selected.profile.language) };
488
+ }
489
+ const data = await tokenManager.callReadOnly((accessToken) => client.a2aTask(taskId, accessToken, { signal: controller.signal }));
490
+ return { data, human: a2aHumanSummary(data, selected.profile.language) };
491
+ }
492
+ finally {
493
+ process.removeListener("SIGINT", onSignal);
494
+ process.removeListener("SIGTERM", onSignal);
495
+ }
496
+ }
360
497
  async function employeesCommand(action, flags) {
361
498
  if (action !== "list")
362
499
  invalid("Use employees list");
@@ -861,7 +998,7 @@ function codexSkillCommand(action, flags) {
861
998
  }
862
999
  async function installCommand(flags, json) {
863
1000
  const allowed = new Set([
864
- "profile", "base-url", "auth-mode", "timeout", "language", "ca-file", "codex-skill-dir",
1001
+ "profile", "base-url", "timeout", "language", "ca-file", "codex-skill-dir",
865
1002
  "skip-login", "skip-codex", "force",
866
1003
  ]);
867
1004
  for (const key of flags.keys()) {
@@ -881,11 +1018,14 @@ async function installCommand(flags, json) {
881
1018
  invalid(`--${key} requires a value`);
882
1019
  return value;
883
1020
  };
1021
+ const controller = new AbortController();
1022
+ const onSignal = () => controller.abort();
1023
+ process.on("SIGINT", onSignal);
1024
+ process.on("SIGTERM", onSignal);
884
1025
  try {
885
1026
  const result = await installWizard({
886
1027
  profile: stringOption("profile"),
887
1028
  baseUrl: stringOption("base-url"),
888
- authMode: stringOption("auth-mode"),
889
1029
  timeout: stringOption("timeout"),
890
1030
  language: stringOption("language"),
891
1031
  caFile: stringOption("ca-file"),
@@ -893,13 +1033,19 @@ async function installCommand(flags, json) {
893
1033
  skipLogin: flags.get("skip-login") === true,
894
1034
  skipCodex: flags.get("skip-codex") === true,
895
1035
  force: flags.get("force") === true,
1036
+ signal: controller.signal,
896
1037
  json,
897
1038
  doctor: (environment, profileName, profile) => doctorData(environment, {
898
1039
  ...(profileName ? { profile: profileName } : {}),
899
1040
  ...(profile ? { profileConfig: profile } : {}),
900
1041
  }),
901
1042
  });
902
- return { ...result };
1043
+ const data = { ...result };
1044
+ // Keep the existing JSON install envelope stable; the locale is used only
1045
+ // for the human summary and is intentionally not a new automation field.
1046
+ if (json)
1047
+ delete data.language;
1048
+ return data;
903
1049
  }
904
1050
  catch (error) {
905
1051
  if (error instanceof InstallError) {
@@ -912,6 +1058,56 @@ async function installCommand(flags, json) {
912
1058
  }
913
1059
  throw error;
914
1060
  }
1061
+ finally {
1062
+ process.removeListener("SIGINT", onSignal);
1063
+ process.removeListener("SIGTERM", onSignal);
1064
+ }
1065
+ }
1066
+ export function installHumanSummary(data) {
1067
+ const zh = data.language === "zh";
1068
+ const profile = typeof data.profile === "string" ? data.profile : "-";
1069
+ const baseUrl = typeof data.base_url === "string" ? data.base_url : "-";
1070
+ const login = data.login?.status === "logged_in"
1071
+ ? (zh ? "已登录" : "logged in")
1072
+ : (zh ? "已跳过" : "skipped");
1073
+ const skillStatus = data.codex?.status;
1074
+ const skill = skillStatus === "unchanged"
1075
+ ? (zh ? "已安装(可重复运行相同命令)" : "already installed (safe to rerun)")
1076
+ : skillStatus === "updated"
1077
+ ? (zh ? "已更新" : "updated")
1078
+ : skillStatus === "skipped"
1079
+ ? (zh ? "已跳过" : "skipped")
1080
+ : (zh ? "已安装" : "installed");
1081
+ if (zh) {
1082
+ return [
1083
+ "安装完成",
1084
+ `Profile:${profile}`,
1085
+ `智护地址:${baseUrl}`,
1086
+ `登录:${login}`,
1087
+ `Codex Skill:${skill}`,
1088
+ "请新开一个 Codex 会话,让它重新发现智护 Skill。",
1089
+ "可以试试:",
1090
+ "- 用智护列出当前活动告警。",
1091
+ "- 用智护查看资产 123 最近一小时 CPU 趋势。",
1092
+ "- 用智护分析告警 456 的根因。",
1093
+ `排查安装:${CLI_DOCTOR_COMMAND}`,
1094
+ `Skill runner:${CLI_RUNNER}`,
1095
+ ].join("\n");
1096
+ }
1097
+ return [
1098
+ "Installation complete",
1099
+ `Profile: ${profile}`,
1100
+ `Pilot: ${baseUrl}`,
1101
+ `Login: ${login}`,
1102
+ `Codex Skill: ${skill}`,
1103
+ "Open a new Codex session so it can discover the Zhihu Skill.",
1104
+ "Try natural-language requests:",
1105
+ "- List the current firing alarms.",
1106
+ "- Show the last hour of CPU trend for asset 123.",
1107
+ "- Diagnose the root cause of alarm 456.",
1108
+ `Troubleshoot installation: ${CLI_DOCTOR_COMMAND}`,
1109
+ `Skill runner: ${CLI_RUNNER}`,
1110
+ ].join("\n");
915
1111
  }
916
1112
  function helpData() {
917
1113
  return {
@@ -924,6 +1120,9 @@ function helpData() {
924
1120
  "auth status",
925
1121
  "auth logout",
926
1122
  "capabilities list",
1123
+ "a2a capabilities",
1124
+ "a2a delegate --capability <id> --message <text>",
1125
+ "a2a task get --id <opaque-task-id>",
927
1126
  "employees list",
928
1127
  "employees chat <employee-id> --message <text>",
929
1128
  "assets list [--keyword <text>] [--model <name|code|id>] [--page N] [--page-size N]",
@@ -933,12 +1132,12 @@ function helpData() {
933
1132
  "alarms get --id <id>",
934
1133
  "incidents diagnose --alarm-id <id>",
935
1134
  "inspections run [--model <name|code|id>] [--window 5m..24h]",
936
- "install [--profile <name>] [--base-url <https-url>] [--auth-mode browser] [--timeout N] [--language zh-CN|en] [--ca-file <absolute-file>] [--codex-skill-dir <absolute-path>] [--skip-login] [--skip-codex] [--force]",
1135
+ "install [--profile <name>] [--base-url <https-url>] [--timeout N] [--language zh-CN|en] [--ca-file <absolute-file>] [--codex-skill-dir <absolute-path>] [--skip-login] [--skip-codex] [--force]",
937
1136
  "install codex [--profile <name>] [--codex-skill-dir <absolute-path>] [--force]",
938
1137
  "uninstall codex [--profile <name>] [--codex-skill-dir <absolute-path>] [--force]",
939
1138
  ],
940
1139
  json: "Append --json for the stable success/error envelope",
941
- note: "Authentication plus read-only employee, asset, metric, alarm, incident diagnosis, inspection queries, local installation, and the Codex Skill installer are available; install also accepts ZHIHU_PROFILE, ZHIHU_BASE_URL, ZHIHU_AUTH_MODE=browser, ZHIHU_CA_BUNDLE, and ZHIHU_TIMEOUT; write operations, MCP, customer URL delivery, and public npm publication remain unavailable",
1140
+ note: "Authentication plus read-only employee, asset, metric, alarm, incident diagnosis, inspection queries, local installation, and the Codex Skill installer are available; login is terminal-only and passwords are never accepted through flags or environment variables; formal GA, write operations, and MCP are not available",
942
1141
  };
943
1142
  }
944
1143
  async function run(parsed) {
@@ -966,7 +1165,8 @@ async function run(parsed) {
966
1165
  return { data: await configInit(parsed.flags), exit_code: 0 };
967
1166
  if (first === "install") {
968
1167
  if (!second && parsed.command.length === 1) {
969
- return { data: await installCommand(parsed.flags, parsed.json), exit_code: 0 };
1168
+ const data = await installCommand(parsed.flags, parsed.json);
1169
+ return { data, exit_code: 0, ...(parsed.json ? {} : { human: installHumanSummary(data) }) };
970
1170
  }
971
1171
  if (second !== "codex" || parsed.command.length !== 2)
972
1172
  invalid("Use install or install codex");
@@ -978,7 +1178,11 @@ async function run(parsed) {
978
1178
  return { data: codexSkillCommand("uninstall", parsed.flags), exit_code: 0 };
979
1179
  }
980
1180
  if (first === "auth")
981
- return { data: await authCommand(second, parsed.flags), exit_code: 0 };
1181
+ return { data: await authCommand(second, parsed.flags, parsed.json), exit_code: 0 };
1182
+ if (first === "a2a") {
1183
+ const result = await a2aCommand(parsed.command, parsed.flags);
1184
+ return { data: result.data, exit_code: 0, ...(parsed.json || !result.human ? {} : { human: result.human }) };
1185
+ }
982
1186
  if (first === "capabilities") {
983
1187
  if (parsed.command.length !== 2)
984
1188
  invalid("Use capabilities list");
@@ -3,7 +3,7 @@ import { createHash, randomBytes } from "node:crypto";
3
3
  import { homedir } from "node:os";
4
4
  import { dirname, isAbsolute, join, parse, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { CLI_VERSION, } from "./contracts.js";
6
+ import { CLI_PACKAGE_NAME, CLI_VERSION, } from "./contracts.js";
7
7
  import { ConfigError, readConfigSnapshot, validateProfile, } from "./config.js";
8
8
  export const CODEX_SKILL_NAME = "zhihu-aiops";
9
9
  export const CODEX_SKILL_VERSION = CLI_VERSION;
@@ -118,7 +118,7 @@ function markerFor(files) {
118
118
  hashes[file] = sha256(files.get(file));
119
119
  return {
120
120
  owner: "zhihu-ai-cli",
121
- package: "@luckypiggy/zhihu-ai-cli",
121
+ package: CLI_PACKAGE_NAME,
122
122
  skill: CODEX_SKILL_NAME,
123
123
  schema: CODEX_SKILL_SCHEMA,
124
124
  schema_version: CODEX_SKILL_SCHEMA,
@@ -219,7 +219,7 @@ function validMarker(value) {
219
219
  if (Object.keys(object).some((key) => !allowedKeys.has(key)))
220
220
  return false;
221
221
  if (object.owner !== "zhihu-ai-cli"
222
- || object.package !== "@luckypiggy/zhihu-ai-cli"
222
+ || object.package !== CLI_PACKAGE_NAME
223
223
  || object.skill !== CODEX_SKILL_NAME
224
224
  || object.schema !== CODEX_SKILL_SCHEMA
225
225
  || object.schema_version !== CODEX_SKILL_SCHEMA
package/dist/config.js CHANGED
@@ -2,6 +2,7 @@ import { homedir } from "node:os";
2
2
  import { dirname, join } from "node:path";
3
3
  import { chmodSync, lstatSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
4
4
  import { CONFIG_SCHEMA_VERSION } from "./contracts.js";
5
+ import { cliCommand } from "./release.js";
5
6
  export const DEFAULT_PROFILE_NAME = "default";
6
7
  export const DEFAULT_PROFILE = {
7
8
  base_url: "",
@@ -110,7 +111,7 @@ function validateBaseUrl(value) {
110
111
  }
111
112
  export function validateProfile(value) {
112
113
  const object = objectOrEmpty(value ?? {}, "Profile configuration");
113
- const allowed = new Set(["base_url", "timeout_seconds", "language", "ca_bundle", "codex_skill_dir"]);
114
+ const allowed = new Set(["base_url", "timeout_seconds", "language", "ca_bundle", "codex_skill_dir", "username", "tenant_id"]);
114
115
  for (const key of Object.keys(object)) {
115
116
  if (!allowed.has(key))
116
117
  throw new ConfigError(`Profile field ${key} is not supported`);
@@ -124,12 +125,22 @@ export function validateProfile(value) {
124
125
  throw new ConfigError("Profile language must be zh or en");
125
126
  const caBundle = optionalString(object, "ca_bundle");
126
127
  const codexSkillDir = optionalString(object, "codex_skill_dir");
128
+ const username = optionalString(object, "username");
129
+ const tenantId = optionalString(object, "tenant_id");
130
+ if (username !== undefined && (username.length === 0 || /[\u0000-\u001f\u007f]/.test(username))) {
131
+ throw new ConfigError("Profile field username is invalid");
132
+ }
133
+ if (tenantId !== undefined && (tenantId.length === 0 || !/^[A-Za-z0-9_-]{1,128}$/.test(tenantId))) {
134
+ throw new ConfigError("Profile field tenant_id is invalid");
135
+ }
127
136
  return {
128
137
  base_url: validateBaseUrl(object.base_url),
129
138
  timeout_seconds: timeout,
130
139
  language,
131
140
  ...(caBundle ? { ca_bundle: caBundle } : {}),
132
141
  ...(codexSkillDir ? { codex_skill_dir: codexSkillDir } : {}),
142
+ ...(username ? { username } : {}),
143
+ ...(tenantId ? { tenant_id: tenantId } : {}),
133
144
  };
134
145
  }
135
146
  /**
@@ -158,13 +169,13 @@ export async function guardProfileConnectionChange(profileName, previous, next,
158
169
  }
159
170
  const credential = await store.get();
160
171
  if (credential) {
161
- throw new ProfileConnectionGuardError("AUTH_ALREADY_LOGGED_IN", `Profile ${profileName} has an active login on another Pilot origin; run zhihu auth logout --profile ${profileName} or use a new profile`);
172
+ throw new ProfileConnectionGuardError("AUTH_ALREADY_LOGGED_IN", `Profile ${profileName} has an active login on another Pilot origin; run ${cliCommand("auth", "logout", "--profile", profileName)} or use a new profile`);
162
173
  }
163
174
  }
164
175
  catch (error) {
165
176
  if (error instanceof ProfileConnectionGuardError)
166
177
  throw error;
167
- throw new ProfileConnectionGuardError("CREDENTIAL_STORE_UNAVAILABLE", `Unable to verify existing credentials for profile ${profileName}; run zhihu auth logout --profile ${profileName} before changing the Pilot origin`);
178
+ throw new ProfileConnectionGuardError("CREDENTIAL_STORE_UNAVAILABLE", `Unable to verify existing credentials for profile ${profileName}; run ${cliCommand("auth", "logout", "--profile", profileName)} before changing the Pilot origin`);
168
179
  }
169
180
  }
170
181
  export function writeJsonAtomic(path, value) {
package/dist/contracts.js CHANGED
@@ -1,4 +1,4 @@
1
- export const CLI_VERSION = "0.1.0-test.1";
1
+ export { CLI_PACKAGE_NAME, CLI_VERSION, CLI_RUNNER, CLI_DOCTOR_COMMAND, cliCommand, } from "./release.js";
2
2
  export const OUTPUT_SCHEMA_VERSION = "1.0";
3
3
  export const CONFIG_SCHEMA_VERSION = "1.0";
4
4
  export const MIN_NODE_MAJOR = 22;
@@ -11,5 +11,7 @@ export const P1_CAPABILITY_SCOPES = [
11
11
  "metrics:read",
12
12
  "alarms:read",
13
13
  "incidents:analyze",
14
+ "a2a:delegate:read",
15
+ "a2a:task:read",
14
16
  "inspections:run-readonly",
15
17
  ];