@gaoding/cli 1.1.0 → 1.2.0

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 (38) hide show
  1. package/dist/bin/gd-cli.js +2 -1
  2. package/dist/bin/insmind-cli.js +60 -0
  3. package/dist/bin/postinstall.js +1 -1
  4. package/dist/src/bootstrap/create-cli.js +14 -4
  5. package/dist/src/bootstrap/create-runtime.js +88 -30
  6. package/dist/src/bootstrap/validators.js +9 -5
  7. package/dist/src/cli/agent-commands.js +5 -4
  8. package/dist/src/cli/auth-commands.js +13 -10
  9. package/dist/src/cli/credits-command.js +23 -0
  10. package/dist/src/cli/dam-commands.js +70 -69
  11. package/dist/src/cli/editor-commands.js +12 -11
  12. package/dist/src/cli/errors.js +49 -9
  13. package/dist/src/cli/model-commands.js +6 -5
  14. package/dist/src/cli/presenter.js +24 -6
  15. package/dist/src/cli/tool-commands.js +7 -6
  16. package/dist/src/cli/update-command.js +4 -3
  17. package/dist/src/features/auth/credential-store.js +1 -1
  18. package/dist/src/features/auth/sso-service.js +3 -1
  19. package/dist/src/features/auth/use-cases.js +38 -12
  20. package/dist/src/features/credits/use-cases.js +52 -0
  21. package/dist/src/features/dam/storage-upload.js +1 -1
  22. package/dist/src/features/editor/bridge-process.js +11 -4
  23. package/dist/src/features/editor/session-state.js +1 -1
  24. package/dist/src/features/editor/session.js +14 -8
  25. package/dist/src/features/org/use-cases.js +7 -0
  26. package/dist/src/features/skill/bundled-skills.js +13 -5
  27. package/dist/src/features/tool/catalog.js +13 -13
  28. package/dist/src/features/tool/use-cases.js +3 -3
  29. package/dist/src/features/update/latest-version.js +2 -2
  30. package/dist/src/features/update/update-notification.js +10 -3
  31. package/dist/src/features/update/update-service.js +57 -23
  32. package/dist/src/platform/signed-http-transport.js +1 -0
  33. package/dist/src/product/billing.js +45 -0
  34. package/dist/src/product/config.js +83 -0
  35. package/dist/src/product/locale.js +38 -0
  36. package/dist/src/product/text.js +3 -0
  37. package/dist/src/telemetry/sls-sink.js +1 -2
  38. package/package.json +5 -5
@@ -1,15 +1,25 @@
1
1
  export class DeviceAuthorizationExpiredError extends Error {
2
- constructor() {
3
- super("授权码已过期,请重新执行 gd-cli auth login。");
2
+ constructor(commandName = "gd-cli") {
3
+ super(`Authorization expired. Run ${commandName} auth login again.`);
4
4
  this.name = "DeviceAuthorizationExpiredError";
5
5
  }
6
6
  }
7
+ export class AuthorizedAccountContextMissingError extends Error {
8
+ constructor() {
9
+ super("The authorized insMind account did not provide its account context.");
10
+ this.name = "AuthorizedAccountContextMissingError";
11
+ }
12
+ }
7
13
  export function createAuthUseCases(dependencies) {
14
+ const insmind = dependencies.commandName === "insmind-cli";
8
15
  return {
9
16
  async status(input) {
10
17
  const state = await dependencies.telemetry.stage("state", () => dependencies.store.read());
11
18
  if (!state)
12
- return { logged_in: false, next_steps: ["gd-cli auth login"] };
19
+ return {
20
+ logged_in: false,
21
+ next_steps: [`${dependencies.commandName ?? "gd-cli"} auth login`]
22
+ };
13
23
  dependencies.telemetry.annotate({
14
24
  account_id: state.account?.id,
15
25
  organization_id: state.organization?.id
@@ -21,7 +31,7 @@ export function createAuthUseCases(dependencies) {
21
31
  user_id: state.account?.id ?? null,
22
32
  name: state.account?.name ?? null
23
33
  },
24
- organization: state.organization
34
+ organization: !dependencies.requireOrganization && state.organization
25
35
  ? { id: state.organization.id, name: state.organization.name }
26
36
  : null,
27
37
  credential: {
@@ -30,10 +40,12 @@ export function createAuthUseCases(dependencies) {
30
40
  scope: [...state.credential.scope]
31
41
  },
32
42
  next_steps: !valid
33
- ? ["gd-cli auth logout", "gd-cli auth login"]
34
- : state.organization
43
+ ? [`${dependencies.commandName ?? "gd-cli"} auth logout`, `${dependencies.commandName ?? "gd-cli"} auth login`]
44
+ : dependencies.requireOrganization
35
45
  ? []
36
- : ["gd-cli org list", "gd-cli org switch"]
46
+ : state.organization
47
+ ? []
48
+ : ["gd-cli org list", "gd-cli org switch"]
37
49
  };
38
50
  },
39
51
  async logout() {
@@ -42,7 +54,9 @@ export function createAuthUseCases(dependencies) {
42
54
  async login(input) {
43
55
  input.signal.throwIfAborted();
44
56
  const existing = await dependencies.telemetry.stage("state", () => dependencies.store.read());
45
- if (existing && Date.parse(existing.credential.expiresAt) > dependencies.now().getTime()) {
57
+ if (existing
58
+ && Date.parse(existing.credential.expiresAt) > dependencies.now().getTime()
59
+ && (!dependencies.requireOrganization || existing.organization !== undefined)) {
46
60
  dependencies.telemetry.annotate({
47
61
  account_id: existing.account?.id,
48
62
  organization_id: existing.organization?.id
@@ -62,7 +76,9 @@ export function createAuthUseCases(dependencies) {
62
76
  await input.interaction.openBrowser(authorizationUrl);
63
77
  }
64
78
  catch {
65
- input.interaction.warn("无法自动打开浏览器,请手动访问授权链接。");
79
+ input.interaction.warn(insmind
80
+ ? "Could not open the browser. Open the authorization URL manually."
81
+ : "无法自动打开浏览器,请手动访问授权链接。");
66
82
  }
67
83
  }
68
84
  return authorization;
@@ -72,7 +88,7 @@ export function createAuthUseCases(dependencies) {
72
88
  const token = await dependencies.telemetry.stage("wait", async () => {
73
89
  while (true) {
74
90
  if (dependencies.now().getTime() + interval > deadline) {
75
- throw new DeviceAuthorizationExpiredError();
91
+ throw new DeviceAuthorizationExpiredError(dependencies.commandName);
76
92
  }
77
93
  await dependencies.sleep(interval, input.signal);
78
94
  input.signal.throwIfAborted();
@@ -91,7 +107,9 @@ export function createAuthUseCases(dependencies) {
91
107
  credential: token.credential,
92
108
  ...(token.account ? { account: token.account } : {})
93
109
  };
94
- await dependencies.telemetry.stage("state", () => dependencies.store.write(state));
110
+ if (!dependencies.requireOrganization) {
111
+ await dependencies.telemetry.stage("state", () => dependencies.store.write(state));
112
+ }
95
113
  dependencies.telemetry.annotate({ account_id: state.account?.id });
96
114
  let organizationId = token.organizationId;
97
115
  try {
@@ -99,7 +117,9 @@ export function createAuthUseCases(dependencies) {
99
117
  organizationId = identity.organizationId ?? organizationId;
100
118
  if (!state.account && identity.account) {
101
119
  state = { ...state, account: identity.account };
102
- await dependencies.telemetry.stage("state", () => dependencies.store.write(state));
120
+ if (!dependencies.requireOrganization) {
121
+ await dependencies.telemetry.stage("state", () => dependencies.store.write(state));
122
+ }
103
123
  dependencies.telemetry.annotate({ account_id: identity.account.id });
104
124
  }
105
125
  }
@@ -108,6 +128,8 @@ export function createAuthUseCases(dependencies) {
108
128
  throw input.signal.reason;
109
129
  }
110
130
  if (!organizationId) {
131
+ if (dependencies.requireOrganization)
132
+ throw new AuthorizedAccountContextMissingError();
111
133
  input.interaction.warn("登录成功,请执行 gd-cli org switch 选择组织。");
112
134
  return { status: "logged_in", organizationSelected: false };
113
135
  }
@@ -118,6 +140,8 @@ export function createAuthUseCases(dependencies) {
118
140
  signal: input.signal
119
141
  })));
120
142
  if (!organization) {
143
+ if (dependencies.requireOrganization)
144
+ throw new AuthorizedAccountContextMissingError();
121
145
  input.interaction.warn("登录成功,但未能选择组织;请执行 gd-cli org switch。");
122
146
  return { status: "logged_in", organizationSelected: false };
123
147
  }
@@ -128,6 +152,8 @@ export function createAuthUseCases(dependencies) {
128
152
  catch {
129
153
  if (input.signal.aborted)
130
154
  throw input.signal.reason;
155
+ if (dependencies.requireOrganization)
156
+ throw new AuthorizedAccountContextMissingError();
131
157
  input.interaction.warn("登录成功,但组织绑定失败;请执行 gd-cli org switch。");
132
158
  return { status: "logged_in", organizationSelected: false };
133
159
  }
@@ -0,0 +1,52 @@
1
+ import { RemoteProtocolError } from "../../platform/remote-protocol.js";
2
+ export function createCreditsUseCases(options) {
3
+ return {
4
+ async get(input) {
5
+ const body = await options.transport.getJson({
6
+ path: "/trade/rights-overview",
7
+ query: { goods_type_enums: "GAODOU" },
8
+ credential: input.credential,
9
+ organizationId: input.organizationId,
10
+ signal: input.signal
11
+ });
12
+ const source = findCreditsRecord(body);
13
+ const total = number(source.total);
14
+ const used = number(source.used);
15
+ const pending = number(source.wait);
16
+ if (total === undefined || used === undefined || pending === undefined) {
17
+ throw new RemoteProtocolError();
18
+ }
19
+ return {
20
+ credits: {
21
+ unit: options.billingUnit,
22
+ total,
23
+ used,
24
+ pending,
25
+ available: Math.max(0, total - used)
26
+ }
27
+ };
28
+ }
29
+ };
30
+ }
31
+ function findCreditsRecord(value) {
32
+ if (Array.isArray(value)) {
33
+ const record = value.find((candidate) => (isRecord(candidate) && candidate.goods_type_enum === "GAODOU"));
34
+ if (!record)
35
+ throw new RemoteProtocolError();
36
+ return record;
37
+ }
38
+ if (!isRecord(value))
39
+ throw new RemoteProtocolError();
40
+ const candidates = [value.GAODOU, value.data, isRecord(value.data) ? value.data.GAODOU : undefined];
41
+ const record = candidates.find(isRecord);
42
+ if (!record)
43
+ throw new RemoteProtocolError();
44
+ return record;
45
+ }
46
+ function number(value) {
47
+ const parsed = typeof value === "number" ? value : Number(value);
48
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
49
+ }
50
+ function isRecord(value) {
51
+ return typeof value === "object" && value !== null && !Array.isArray(value);
52
+ }
@@ -11,7 +11,7 @@ function requiredString(record, key) {
11
11
  }
12
12
  function optionalString(record, key) {
13
13
  const value = record[key];
14
- if (value === undefined)
14
+ if (value === undefined || value === null)
15
15
  return undefined;
16
16
  if (typeof value !== "string" || value.trim() === "")
17
17
  throw new RemoteRequestError();
@@ -9,7 +9,10 @@ export async function runEditorBridgeProcess(targetOrigin, options = {}) {
9
9
  const store = createEditorSessionStateStore({
10
10
  ...(options.homeDirectory === undefined
11
11
  ? {}
12
- : { homeDirectory: options.homeDirectory })
12
+ : { homeDirectory: options.homeDirectory }),
13
+ ...(options.stateDirectory === undefined
14
+ ? {}
15
+ : { stateDirectory: options.stateDirectory })
13
16
  });
14
17
  const bridge = await (options.startBridge ?? startEditorBridge)({ targetOrigin });
15
18
  const shutdown = () => {
@@ -52,7 +55,9 @@ export function startEditorBridgeProcess(input) {
52
55
  child = (input.spawn ?? nodeSpawn)(process.execPath, [
53
56
  fileURLToPath(new URL("./bridge-process.js", import.meta.url)),
54
57
  input.targetOrigin,
55
- ...(input.homeDirectory === undefined ? [] : [input.homeDirectory])
58
+ ...(input.stateDirectory === undefined
59
+ ? (input.homeDirectory === undefined ? [] : [input.homeDirectory])
60
+ : [input.homeDirectory ?? "", input.stateDirectory])
56
61
  ], {
57
62
  detached: true,
58
63
  stdio: ["ignore", "ignore", "ignore", "ipc"]
@@ -208,13 +213,15 @@ function hasExactKeys(value, keys) {
208
213
  const ownPath = fileURLToPath(import.meta.url);
209
214
  if (process.argv[1] && resolve(process.argv[1]) === resolve(ownPath)) {
210
215
  const targetOrigin = process.argv[2];
211
- const homeDirectory = process.argv[3];
216
+ const homeDirectory = process.argv[3] || undefined;
217
+ const stateDirectory = process.argv[4];
212
218
  if (!targetOrigin) {
213
219
  process.exitCode = 1;
214
220
  }
215
221
  else {
216
222
  void runEditorBridgeProcess(targetOrigin, {
217
- ...(homeDirectory === undefined ? {} : { homeDirectory })
223
+ ...(homeDirectory === undefined ? {} : { homeDirectory }),
224
+ ...(stateDirectory === undefined ? {} : { stateDirectory })
218
225
  }).catch(() => {
219
226
  process.exitCode = 1;
220
227
  });
@@ -3,7 +3,7 @@ import { join } from "node:path";
3
3
  import { createLocalJsonFile } from "../../platform/local-json-file.js";
4
4
  export function createEditorSessionStateStore(options = {}) {
5
5
  const file = options.file ?? createLocalJsonFile({
6
- directory: join(options.homeDirectory ?? homedir(), ".gd"),
6
+ directory: join(options.homeDirectory ?? homedir(), options.stateDirectory ?? ".gd"),
7
7
  filename: "editor-session.json"
8
8
  });
9
9
  const read = async () => {
@@ -14,8 +14,8 @@ export class EditorTargetError extends Error {
14
14
  }
15
15
  export class EditorSessionNotFoundError extends Error {
16
16
  code = "EDITOR_SESSION_NOT_FOUND";
17
- constructor() {
18
- super("当前没有可用的 Editor 会话,请重新执行 gd-cli editor connect [target]。");
17
+ constructor(commandName = "gd-cli") {
18
+ super(`当前没有可用的 Editor 会话,请重新执行 ${commandName} editor connect [target]。`);
19
19
  this.name = "EditorSessionNotFoundError";
20
20
  }
21
21
  }
@@ -27,7 +27,7 @@ export class EditorNotReadyError extends Error {
27
27
  }
28
28
  }
29
29
  export function resolveEditorTarget(value) {
30
- return resolveTarget(value, DEFAULT_CREATE_URL, DEFAULT_WORK_URL).href;
30
+ return resolveTarget(value, DEFAULT_CREATE_URL, DEFAULT_WORK_URL, false).href;
31
31
  }
32
32
  export function createEditorSession(dependencies) {
33
33
  const startProcess = dependencies.startProcess ?? startEditorBridgeProcess;
@@ -37,7 +37,7 @@ export function createEditorSession(dependencies) {
37
37
  return {
38
38
  async connect(target, signal) {
39
39
  signal.throwIfAborted();
40
- const targetUrl = resolveTarget(target, dependencies.defaultCreateUrl, dependencies.workUrl);
40
+ const targetUrl = resolveTarget(target, dependencies.defaultCreateUrl, dependencies.workUrl, dependencies.restrictTargets === true);
41
41
  await replaceCurrentSession(signal);
42
42
  signal.throwIfAborted();
43
43
  const state = await startProcess({
@@ -45,6 +45,9 @@ export function createEditorSession(dependencies) {
45
45
  ...(dependencies.homeDirectory === undefined
46
46
  ? {}
47
47
  : { homeDirectory: dependencies.homeDirectory }),
48
+ ...(dependencies.stateDirectory === undefined
49
+ ? {}
50
+ : { stateDirectory: dependencies.stateDirectory }),
48
51
  signal
49
52
  });
50
53
  const handoffUrl = buildHandoffUrl(targetUrl, state);
@@ -82,7 +85,7 @@ export function createEditorSession(dependencies) {
82
85
  signal.throwIfAborted();
83
86
  const state = await dependencies.store.read();
84
87
  if (state === null)
85
- throw new EditorSessionNotFoundError();
88
+ throw new EditorSessionNotFoundError(dependencies.commandName);
86
89
  try {
87
90
  return await createClient(state).rpc(method, payload, signal);
88
91
  }
@@ -96,7 +99,7 @@ export function createEditorSession(dependencies) {
96
99
  if (!(cause instanceof EditorBridgeUnavailableError))
97
100
  throw cause;
98
101
  await dependencies.store.removeIfOwned(state.token);
99
- throw new EditorSessionNotFoundError();
102
+ throw new EditorSessionNotFoundError(dependencies.commandName);
100
103
  }
101
104
  async function replaceCurrentSession(signal) {
102
105
  const current = await dependencies.store.read();
@@ -154,7 +157,7 @@ export function createEditorSession(dependencies) {
154
157
  }
155
158
  }
156
159
  }
157
- function resolveTarget(value, createUrl, workUrl) {
160
+ function resolveTarget(value, createUrl, workUrl, restrictTargets) {
158
161
  if (value === undefined)
159
162
  return new URL(createUrl.href);
160
163
  if (/^\d+$/.test(value)) {
@@ -176,7 +179,10 @@ function resolveTarget(value, createUrl, workUrl) {
176
179
  }
177
180
  if ((url.protocol !== "http:" && url.protocol !== "https:") ||
178
181
  url.username !== "" ||
179
- url.password !== "") {
182
+ url.password !== "" ||
183
+ (restrictTargets && (url.protocol !== "https:" ||
184
+ url.origin !== workUrl.origin ||
185
+ url.pathname !== workUrl.pathname))) {
180
186
  throw new EditorTargetError();
181
187
  }
182
188
  return url;
@@ -98,6 +98,13 @@ export function createOrgUseCases(dependencies) {
98
98
  if (!organizationId)
99
99
  return null;
100
100
  try {
101
+ if (dependencies.singleOrganizationName !== undefined) {
102
+ return await bind(input.credential, {
103
+ id: organizationId,
104
+ publicName: dependencies.singleOrganizationName,
105
+ realName: dependencies.singleOrganizationName
106
+ }, input.signal);
107
+ }
101
108
  const records = await dependencies.org.list(input.credential, input.signal);
102
109
  const record = records.find((candidate) => candidate.id === organizationId);
103
110
  if (!record?.realName)
@@ -14,16 +14,17 @@ export function loadBundledSkills(startFile) {
14
14
  catch (error) {
15
15
  throw new SkillSyncError(`无法读取 @gaoding/cli package metadata: ${message(error)}`, []);
16
16
  }
17
- if (metadata.name === "@gaoding/cli") {
17
+ const product = packageProduct(metadata.name);
18
+ if (product) {
18
19
  if (typeof metadata.version !== "string" || !metadata.version.trim()) {
19
- throw new SkillSyncError("@gaoding/cli package version 无效。", []);
20
+ throw new SkillSyncError(`${product.packageName} package version is invalid.`, []);
20
21
  }
21
- const sourceDirectory = join(directory, "skills", "gd-cli");
22
+ const sourceDirectory = join(directory, "skills", product.skillName);
22
23
  if (!isFile(join(sourceDirectory, "SKILL.md"))) {
23
24
  throw new SkillSyncError(`Agent Skill source 不存在: ${sourceDirectory}`, []);
24
25
  }
25
26
  return [{
26
- skill: "gd-cli",
27
+ skill: product.skillName,
27
28
  version: metadata.version,
28
29
  sourceDirectory
29
30
  }];
@@ -31,7 +32,14 @@ export function loadBundledSkills(startFile) {
31
32
  }
32
33
  directory = dirname(directory);
33
34
  }
34
- throw new SkillSyncError("无法定位 @gaoding/cli package root", []);
35
+ throw new SkillSyncError("Unable to locate a supported CLI package root.", []);
36
+ }
37
+ function packageProduct(name) {
38
+ if (name === "@gaoding/cli")
39
+ return { packageName: name, skillName: "gd-cli" };
40
+ if (name === "@insmind/cli")
41
+ return { packageName: name, skillName: "insmind-cli" };
42
+ return null;
35
43
  }
36
44
  function directoryFrom(path) {
37
45
  const absolute = resolve(path);
@@ -1,10 +1,4 @@
1
- export class ToolInputError extends Error {
2
- constructor() {
3
- super("Tool 输入不可用。");
4
- this.name = "ToolInputError";
5
- }
6
- }
7
- const tools = [
1
+ export const defaultToolDefinitions = [
8
2
  {
9
3
  name: "image.generate",
10
4
  title: "图片生成与编辑",
@@ -21,11 +15,17 @@ const tools = [
21
15
  description: "使用已发布的文本模型,根据文本和可选图片生成文本。"
22
16
  }
23
17
  ];
24
- function toolIndex(tool) {
18
+ export class ToolInputError extends Error {
19
+ constructor() {
20
+ super("Tool 输入不可用。");
21
+ this.name = "ToolInputError";
22
+ }
23
+ }
24
+ function toolIndex(tool, tools) {
25
25
  return tools.findIndex((candidate) => candidate.name === tool);
26
26
  }
27
- function compareModels(left, right) {
28
- return toolIndex(left.tool) - toolIndex(right.tool)
27
+ function compareModels(left, right, tools) {
28
+ return toolIndex(left.tool, tools) - toolIndex(right.tool, tools)
29
29
  || left.sceneSort - right.sceneSort
30
30
  || left.modelSort - right.modelSort
31
31
  || (left.model < right.model ? -1 : left.model > right.model ? 1 : 0);
@@ -40,19 +40,19 @@ function summary(model) {
40
40
  ...(model.estimatedTime === undefined ? {} : { estimatedTime: model.estimatedTime })
41
41
  };
42
42
  }
43
- export function projectToolList(catalog) {
43
+ export function projectToolList(catalog, tools = defaultToolDefinitions) {
44
44
  return {
45
45
  tools: tools.filter((tool) => catalog.models.some((model) => model.tool === tool.name))
46
46
  };
47
47
  }
48
- export function projectModelList(catalog, tool) {
48
+ export function projectModelList(catalog, tool, tools = defaultToolDefinitions) {
49
49
  if (tool !== undefined && !catalog.models.some((model) => model.tool === tool)) {
50
50
  throw new ToolInputError();
51
51
  }
52
52
  return {
53
53
  models: catalog.models
54
54
  .filter((model) => tool === undefined || model.tool === tool)
55
- .toSorted(compareModels)
55
+ .toSorted((left, right) => compareModels(left, right, tools))
56
56
  .map(summary)
57
57
  };
58
58
  }
@@ -1,5 +1,5 @@
1
1
  import { parseSafeRemoteAssetUrl, UrlSafetyError } from "../../platform/url-safety.js";
2
- import { findToolModel, projectModelDetail, projectModelList, projectToolList, ToolInputError } from "./catalog.js";
2
+ import { findToolModel, defaultToolDefinitions, projectModelDetail, projectModelList, projectToolList, ToolInputError } from "./catalog.js";
3
3
  import { assertModelArguments, buildToolArgumentsSchema } from "./dynamic-schema.js";
4
4
  const mediaTypes = {
5
5
  avif: "image/avif",
@@ -56,10 +56,10 @@ export function createToolUseCases(dependencies) {
56
56
  }
57
57
  return {
58
58
  async listTools({ access, signal }) {
59
- return projectToolList(await load(access, signal));
59
+ return projectToolList(await load(access, signal), dependencies.tools ?? defaultToolDefinitions);
60
60
  },
61
61
  async listModels({ input, access, signal }) {
62
- return projectModelList(await load(access, signal), input.tool);
62
+ return projectModelList(await load(access, signal), input.tool, dependencies.tools ?? defaultToolDefinitions);
63
63
  },
64
64
  async getModel({ input, access, signal }) {
65
65
  return projectModelDetail(await load(access, signal), input.model);
@@ -1,12 +1,12 @@
1
1
  import { valid } from "semver";
2
- const REGISTRY_DIST_TAGS_URL = "https://registry.npmjs.org/@gaoding%2fcli";
2
+ export const GAODING_REGISTRY_DIST_TAGS_URL = "https://registry.npmjs.org/@gaoding%2fcli";
3
3
  export async function fetchLatestVersion(options) {
4
4
  const headers = new Headers({
5
5
  Accept: "application/vnd.npm.install-v1+json"
6
6
  });
7
7
  if (options.etag)
8
8
  headers.set("If-None-Match", options.etag);
9
- const response = await options.fetch(REGISTRY_DIST_TAGS_URL, {
9
+ const response = await options.fetch(options.registryUrl ?? GAODING_REGISTRY_DIST_TAGS_URL, {
10
10
  headers,
11
11
  redirect: "error",
12
12
  signal: options.signal
@@ -13,7 +13,7 @@ export function createUpdateNotificationService(options) {
13
13
  const fetchPackage = options.fetch ?? globalThis.fetch;
14
14
  const now = options.now ?? (() => new Date());
15
15
  const env = options.env ?? process.env;
16
- const stateDirectory = join(options.homeDirectory ?? homedir(), ".gd");
16
+ const stateDirectory = join(options.homeDirectory ?? homedir(), options.stateDirectoryName ?? ".gd");
17
17
  const stateFile = options.stateFile ?? createLocalJsonFile({
18
18
  directory: stateDirectory,
19
19
  filename: "update-check.json"
@@ -52,6 +52,7 @@ export function createUpdateNotificationService(options) {
52
52
  latestResult = await fetchLatestVersion({
53
53
  fetch: fetchPackage,
54
54
  signal: timeout.signal,
55
+ ...(options.registryUrl ? { registryUrl: options.registryUrl } : {}),
55
56
  ...(state.etag ? { etag: state.etag } : {})
56
57
  });
57
58
  }
@@ -231,8 +232,14 @@ function withReminder(state, today, version) {
231
232
  };
232
233
  }
233
234
  function notify(options, latest) {
234
- options.notify(`gd-cli 有新版本 ${latest},当前版本为 ${options.currentVersion}。` +
235
- "运行 gd-cli update 即可更新。");
235
+ if (options.commandName && options.commandName !== "gd-cli") {
236
+ options.notify(`${options.commandName} ${latest} is available; current version is ` +
237
+ `${options.currentVersion}. Run ${options.commandName} update to update.`);
238
+ }
239
+ else {
240
+ options.notify(`gd-cli 有新版本 ${latest},当前版本为 ${options.currentVersion}。` +
241
+ "运行 gd-cli update 即可更新。");
242
+ }
236
243
  }
237
244
  function isNewer(version, currentVersion) {
238
245
  return version !== undefined && valid(version) !== null && gt(version, currentVersion);