@gaoding/cli 1.0.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 (40) 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 +31 -4
  4. package/dist/src/bootstrap/create-cli.js +14 -4
  5. package/dist/src/bootstrap/create-runtime.js +93 -31
  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 +12 -3
  31. package/dist/src/features/update/update-service.js +64 -21
  32. package/dist/src/platform/installation-owner.js +81 -0
  33. package/dist/src/platform/signed-http-transport.js +1 -0
  34. package/dist/src/product/billing.js +45 -0
  35. package/dist/src/product/config.js +83 -0
  36. package/dist/src/product/locale.js +38 -0
  37. package/dist/src/product/text.js +3 -0
  38. package/dist/src/telemetry/sls-sink.js +1 -2
  39. package/package.json +5 -5
  40. package/skills/gd-cli/references/update.md +2 -0
@@ -27,7 +27,8 @@ try {
27
27
  signal: controller.signal,
28
28
  telemetry: runtime.telemetry,
29
29
  telemetrySink: runtime.telemetrySink,
30
- updateNotification: runtime.updateNotification
30
+ updateNotification: runtime.updateNotification,
31
+ product: runtime.product
31
32
  });
32
33
  }
33
34
  catch (error) {
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import { insmindProduct } from "../src/product/config.js";
3
+ import { localeOption } from "../src/product/locale.js";
4
+ const controller = new AbortController();
5
+ const interrupt = () => controller.abort(new Error("SIGINT"));
6
+ process.once("SIGINT", interrupt);
7
+ let runtime;
8
+ let mapCliError;
9
+ let redact;
10
+ try {
11
+ const [cliModule, runtimeModule, errorsModule, redactModule] = await Promise.all([
12
+ import("../src/bootstrap/create-cli.js"),
13
+ import("../src/bootstrap/create-runtime.js"),
14
+ import("../src/cli/errors.js"),
15
+ import("../src/platform/redact.js")
16
+ ]);
17
+ mapCliError = errorsModule.mapCliError;
18
+ redact = redactModule.redact;
19
+ const selectedLocale = localeOption(process.argv.slice(2));
20
+ runtime = runtimeModule.createProductionRuntime({
21
+ product: insmindProduct,
22
+ ...(selectedLocale === undefined ? {} : { locale: selectedLocale })
23
+ });
24
+ const program = cliModule.createCli(runtime, { signal: controller.signal });
25
+ process.exitCode = await cliModule.executeCli({
26
+ program,
27
+ argv: process.argv.slice(2),
28
+ presenter: runtime.presenter,
29
+ signal: controller.signal,
30
+ telemetry: runtime.telemetry,
31
+ telemetrySink: runtime.telemetrySink,
32
+ updateNotification: runtime.updateNotification,
33
+ product: runtime.product
34
+ });
35
+ }
36
+ catch (error) {
37
+ if (mapCliError === undefined || redact === undefined) {
38
+ process.stderr.write("INTERNAL_ERROR: CLI failed to start.\n");
39
+ process.exitCode = 1;
40
+ }
41
+ else {
42
+ const outcome = redact(mapCliError(error, { aborted: controller.signal.aborted, product: insmindProduct }));
43
+ if (runtime) {
44
+ runtime.presenter.error(outcome, { json: process.argv.includes("--json") });
45
+ }
46
+ else if (outcome.kind === "interrupted") {
47
+ process.stderr.write("Cancelled.\n");
48
+ }
49
+ else if (process.argv.includes("--json")) {
50
+ process.stderr.write(`${JSON.stringify({ error: outcome.failure })}\n`);
51
+ }
52
+ else {
53
+ process.stderr.write(`${outcome.failure.code}: ${outcome.failure.message}\n`);
54
+ }
55
+ process.exitCode = outcome.exitCode;
56
+ }
57
+ }
58
+ finally {
59
+ process.removeListener("SIGINT", interrupt);
60
+ }
@@ -1,7 +1,8 @@
1
1
  import { fileURLToPath } from "node:url";
2
- import { resolve } from "node:path";
2
+ import { dirname, resolve } from "node:path";
3
3
  import { loadBundledSkills } from "../src/features/skill/bundled-skills.js";
4
4
  import { syncBundledSkills } from "../src/features/skill/installer.js";
5
+ import { markWorkBuddyInstallation, readInstallationOwner } from "../src/platform/installation-owner.js";
5
6
  export function isSupportedGlobalLifecycle(env) {
6
7
  const userAgent = env.npm_config_user_agent ?? "";
7
8
  if (env.npm_config_global === "true") {
@@ -11,7 +12,7 @@ export function isSupportedGlobalLifecycle(env) {
11
12
  return userAgent.startsWith("pnpm/")
12
13
  && env.npm_command === "add"
13
14
  && env.npm_lifecycle_event === "postinstall"
14
- && env.npm_package_name === "@gaoding/cli"
15
+ && (env.npm_package_name === "@gaoding/cli" || env.npm_package_name === "@insmind/cli")
15
16
  && /\/v\d+\/[0-9a-f]+-[0-9a-f]+-[0-9a-f]{16}$/u.test(initialDirectory);
16
17
  }
17
18
  export async function runPostinstall(options = {}) {
@@ -19,8 +20,31 @@ export async function runPostinstall(options = {}) {
19
20
  if (!isSupportedGlobalLifecycle(env))
20
21
  return;
21
22
  const writeError = options.writeError ?? ((text) => process.stderr.write(text));
23
+ let skills;
24
+ try {
25
+ skills = (options.loadSkills ?? loadBundledSkills)(options.startFile ?? fileURLToPath(import.meta.url));
26
+ }
27
+ catch (error) {
28
+ if (env.GD_CLI_INSTALL_OWNER === "workbuddy") {
29
+ throw new Error("无法确认当前安装的 package。");
30
+ }
31
+ writeError(`警告: ${oneLineMessage(error)}\n`);
32
+ return;
33
+ }
34
+ const primarySkill = skills.find((skill) => skill.skill === "gd-cli");
35
+ if (!primarySkill)
36
+ throw new Error("Bundled gd-cli Skill is missing.");
37
+ const packageRoot = dirname(dirname(primarySkill.sourceDirectory));
38
+ if (env.GD_CLI_INSTALL_OWNER === "workbuddy") {
39
+ markWorkBuddyInstallation(packageRoot);
40
+ return;
41
+ }
42
+ const owner = readInstallationOwner(packageRoot);
43
+ if (owner === "workbuddy")
44
+ return;
45
+ if (owner === "invalid")
46
+ throw new Error("无法确认当前安装的托管信息。");
22
47
  try {
23
- const skills = (options.loadSkills ?? loadBundledSkills)(options.startFile ?? fileURLToPath(import.meta.url));
24
48
  await (options.syncSkills ?? syncBundledSkills)(skills, {
25
49
  env,
26
50
  ...(options.homeDirectory ? { homeDirectory: options.homeDirectory } : {})
@@ -38,5 +62,8 @@ const entryPath = process.argv[1];
38
62
  const isDirectEntry = entryPath !== undefined
39
63
  && resolve(entryPath) === fileURLToPath(import.meta.url);
40
64
  if (isDirectEntry || process.env.npm_lifecycle_event === "postinstall") {
41
- void runPostinstall();
65
+ void runPostinstall().catch((error) => {
66
+ process.stderr.write(`${oneLineMessage(error)}\n`);
67
+ process.exitCode = 1;
68
+ });
42
69
  }
@@ -9,12 +9,13 @@ import { registerModelCommands } from "../cli/model-commands.js";
9
9
  import { registerEditorCommands } from "../cli/editor-commands.js";
10
10
  import { registerDamCommands } from "../cli/dam-commands.js";
11
11
  import { registerUpdateCommand } from "../cli/update-command.js";
12
+ import { registerCreditsCommand } from "../cli/credits-command.js";
12
13
  import { redact } from "../platform/redact.js";
13
14
  export { assertAllLeafCommandsBound, bindAction } from "../cli/action-binding.js";
14
15
  export function createCli(runtime, options) {
15
16
  const program = new Command()
16
- .name("gd-cli")
17
- .description("稿定命令行工具")
17
+ .name(runtime.product.commandName)
18
+ .description(runtime.product.description)
18
19
  .version(runtime.version)
19
20
  .helpCommand(false)
20
21
  .showHelpAfterError(false)
@@ -25,6 +26,9 @@ export function createCli(runtime, options) {
25
26
  writeOut: runtime.io.writeOut,
26
27
  writeErr: () => undefined
27
28
  });
29
+ if (runtime.product.id === "insmind") {
30
+ program.option("--locale <locale>", "locale for service-owned business descriptions");
31
+ }
28
32
  configureActionEnvironment(program, {
29
33
  policies: runtime.accessPolicies,
30
34
  signal: options.signal,
@@ -32,7 +36,10 @@ export function createCli(runtime, options) {
32
36
  updateNotification: runtime.updateNotification
33
37
  });
34
38
  registerAuthCommands(program, runtime);
35
- registerOrgCommands(program, runtime);
39
+ if (runtime.product.exposesOrganizations)
40
+ registerOrgCommands(program, runtime);
41
+ else
42
+ registerCreditsCommand(program, runtime);
36
43
  registerAgentCommands(program, runtime);
37
44
  registerToolCommands(program, runtime);
38
45
  registerModelCommands(program, runtime);
@@ -68,7 +75,10 @@ export async function executeCli(options) {
68
75
  // The original Commander error is mapped and presented below.
69
76
  }
70
77
  }
71
- const outcome = redact(mapCliError(error, { aborted: options.signal.aborted }));
78
+ const outcome = redact(mapCliError(error, {
79
+ aborted: options.signal.aborted,
80
+ ...(options.product ? { product: options.product } : {})
81
+ }));
72
82
  const telemetryError = classifyTelemetryError(error);
73
83
  const traceId = options.telemetry.selected
74
84
  ? options.telemetry.traceId
@@ -25,6 +25,7 @@ import { createToolApiAdapter } from "../features/tool/tool-api-adapter.js";
25
25
  import { createToolUseCases } from "../features/tool/use-cases.js";
26
26
  import { createOrgUseCases } from "../features/org/use-cases.js";
27
27
  import { createOrgService } from "../features/org/org-service.js";
28
+ import { createCreditsUseCases } from "../features/credits/use-cases.js";
28
29
  import { createBrowserOpener } from "../platform/open-browser.js";
29
30
  import { createSignedHttpTransport } from "../platform/signed-http-transport.js";
30
31
  import { createJsonInputReader } from "../platform/json-input.js";
@@ -37,22 +38,36 @@ import { createUpdateNotificationService } from "../features/update/update-notif
37
38
  import { classifyTelemetryError } from "../cli/errors.js";
38
39
  import { createTelemetryInvocation } from "../telemetry/invocation.js";
39
40
  import { createSlsTelemetrySink } from "../telemetry/sls-sink.js";
40
- const productionEndpoints = {
41
- orgApi: new URL("https://gdcli.gaoding.com/api"),
42
- agentApi: new URL("https://gdcli.gaoding.com/api"),
43
- mnsApi: new URL("https://gdcli.gaoding.com/api"),
44
- toolApi: new URL("https://gdcli.gaoding.com/api"),
45
- damApi: new URL("https://gdcli.gaoding.com/api"),
46
- ssoApi: new URL("https://www.gaoding.com/api/sso"),
47
- authPageOrigin: new URL("https://www.gaoding.art")
48
- };
41
+ import { gaodingProduct } from "../product/config.js";
42
+ import { resolveProductLocale, systemLocale } from "../product/locale.js";
43
+ import { withAgentBillingUnit, withToolBillingUnit } from "../product/billing.js";
44
+ import { readInstallationOwner } from "../platform/installation-owner.js";
49
45
  export function createProductionRuntime(options = {}) {
46
+ const product = options.product ?? gaodingProduct;
50
47
  const bundledSkills = loadBundledSkills(fileURLToPath(import.meta.url));
51
- const primarySkill = bundledSkills.find((skill) => skill.skill === "gd-cli");
48
+ const primarySkill = bundledSkills.find((skill) => skill.skill === product.skillName);
52
49
  if (!primarySkill)
53
- throw new Error("Bundled gd-cli Skill is missing.");
54
- const endpoints = options.endpoints ?? productionEndpoints;
50
+ throw new Error(`Bundled ${product.skillName} Skill is missing.`);
51
+ const packageRoot = dirname(dirname(primarySkill.sourceDirectory));
52
+ const installationOwner = readInstallationOwner(packageRoot);
53
+ const endpoints = options.endpoints ?? product.endpoints;
55
54
  const fetch = options.fetch ?? globalThis.fetch;
55
+ const env = options.env ?? process.env;
56
+ const environmentLocale = product.localeEnvironmentVariable
57
+ ? env[product.localeEnvironmentVariable]
58
+ : undefined;
59
+ const localSystemLocale = systemLocale();
60
+ const locale = resolveProductLocale({
61
+ ...(options.locale === undefined ? {} : { explicit: options.locale }),
62
+ ...(environmentLocale === undefined ? {} : { environment: environmentLocale }),
63
+ ...(localSystemLocale === undefined ? {} : { system: localSystemLocale })
64
+ });
65
+ const businessHeaders = product.id === "insmind" ? {
66
+ "X-Locale": locale.locale,
67
+ "X-Language": locale.language,
68
+ "X-Market": locale.market,
69
+ "X-Legal-Region": locale.legalRegion
70
+ } : undefined;
56
71
  const now = options.now ?? (() => new Date());
57
72
  const telemetry = createTelemetryInvocation({
58
73
  cliVersion: primarySkill.version,
@@ -66,7 +81,10 @@ export function createProductionRuntime(options = {}) {
66
81
  },
67
82
  classifyError: classifyTelemetryError
68
83
  });
69
- const telemetrySink = createSlsTelemetrySink({ fetch });
84
+ const telemetrySink = createSlsTelemetrySink({
85
+ fetch,
86
+ trackingUrl: product.telemetryUrl
87
+ });
70
88
  const rawIo = options.io ?? {
71
89
  input: process.stdin,
72
90
  stdout: process.stdout,
@@ -81,41 +99,55 @@ export function createProductionRuntime(options = {}) {
81
99
  stderrTerminal: rawIo.stderrTerminal ?? hasTerminal(rawIo.stderr)
82
100
  };
83
101
  const store = createLocalCredentialStore({
84
- ...(options.homeDirectory ? { homeDirectory: options.homeDirectory } : {})
102
+ ...(options.homeDirectory ? { homeDirectory: options.homeDirectory } : {}),
103
+ stateDirectory: product.stateDirectory
85
104
  });
86
105
  const orgTransport = createSignedHttpTransport({
87
106
  baseUrl: endpoints.orgApi,
88
107
  fetch,
89
108
  now,
90
- traceparent: () => telemetry.traceparent()
109
+ traceparent: () => telemetry.traceparent(),
110
+ ...(businessHeaders ? { businessHeaders } : {})
111
+ });
112
+ const creditsTransport = createSignedHttpTransport({
113
+ baseUrl: endpoints.creditsApi,
114
+ fetch,
115
+ now,
116
+ traceparent: () => telemetry.traceparent(),
117
+ ...(businessHeaders ? { businessHeaders } : {})
91
118
  });
92
119
  const agentTransport = createSignedHttpTransport({
93
120
  baseUrl: endpoints.agentApi,
94
121
  fetch,
95
122
  now,
96
- traceparent: () => telemetry.traceparent()
123
+ traceparent: () => telemetry.traceparent(),
124
+ ...(businessHeaders ? { businessHeaders } : {})
97
125
  });
98
126
  const mnsTransport = createSignedHttpTransport({
99
127
  baseUrl: endpoints.mnsApi,
100
128
  fetch,
101
129
  now,
102
- traceparent: () => telemetry.traceparent()
130
+ traceparent: () => telemetry.traceparent(),
131
+ ...(businessHeaders ? { businessHeaders } : {})
103
132
  });
104
133
  const toolTransport = createSignedHttpTransport({
105
134
  baseUrl: endpoints.toolApi,
106
135
  fetch,
107
136
  now,
108
- traceparent: () => telemetry.traceparent()
137
+ traceparent: () => telemetry.traceparent(),
138
+ ...(businessHeaders ? { businessHeaders } : {})
109
139
  });
110
140
  const damTransport = createSignedHttpTransport({
111
141
  baseUrl: endpoints.damApi,
112
142
  fetch,
113
143
  now,
114
144
  channelId: "32",
115
- traceparent: () => telemetry.traceparent()
145
+ traceparent: () => telemetry.traceparent(),
146
+ ...(businessHeaders ? { businessHeaders } : {})
116
147
  });
117
148
  const sso = createSsoService({
118
149
  ssoApi: endpoints.ssoApi,
150
+ clientId: product.deviceClientId,
119
151
  fetch,
120
152
  now
121
153
  });
@@ -123,13 +155,18 @@ export function createProductionRuntime(options = {}) {
123
155
  store,
124
156
  org: createOrgService({ transport: orgTransport }),
125
157
  sso,
126
- telemetry
158
+ telemetry,
159
+ ...(product.exposesOrganizations
160
+ ? {}
161
+ : { singleOrganizationName: product.displayName })
127
162
  });
128
163
  const auth = createAuthUseCases({
129
164
  store,
130
165
  sso,
131
166
  organizationBinder: org,
132
167
  authPageOrigin: endpoints.authPageOrigin,
168
+ commandName: product.commandName,
169
+ requireOrganization: !product.exposesOrganizations,
133
170
  now,
134
171
  telemetry,
135
172
  sleep: async (milliseconds, signal) => {
@@ -137,6 +174,10 @@ export function createProductionRuntime(options = {}) {
137
174
  }
138
175
  });
139
176
  const storage = options.objectStorage ?? createObjectStorageUploader({ fetch, now });
177
+ const credits = createCreditsUseCases({
178
+ transport: creditsTransport,
179
+ billingUnit: product.billingUnit
180
+ });
140
181
  const storageUpload = createDamStorageUploader({
141
182
  transport: damTransport,
142
183
  storage
@@ -144,10 +185,13 @@ export function createProductionRuntime(options = {}) {
144
185
  const transientUploader = createDamTransientUploader({ storageUpload });
145
186
  const presenter = createPresenter({
146
187
  writeOut: io.writeOut,
147
- writeError: io.writeError
188
+ writeError: io.writeError,
189
+ product
148
190
  });
149
191
  const browser = createBrowserOpener();
150
- const validators = createContractValidators();
192
+ const validators = createContractValidators({
193
+ organizations: product.exposesOrganizations
194
+ });
151
195
  const modelCatalog = createMnsCatalogAdapter({ transport: mnsTransport });
152
196
  const damApi = createDamApiAdapter({ transport: damTransport });
153
197
  const damRepositories = createDamRepositoryCatalog({ transport: damTransport });
@@ -190,7 +234,7 @@ export function createProductionRuntime(options = {}) {
190
234
  },
191
235
  telemetry
192
236
  });
193
- const agent = createAgentUseCases({
237
+ const agent = withAgentBillingUnit(createAgentUseCases({
194
238
  catalog: modelCatalog,
195
239
  uploader: transientUploader,
196
240
  completion: createCreativeAgentAdapter({
@@ -200,24 +244,29 @@ export function createProductionRuntime(options = {}) {
200
244
  }),
201
245
  warn: (message) => { presenter.warning(message); },
202
246
  telemetry
203
- });
204
- const tool = createToolUseCases({
247
+ }), product.billingUnit);
248
+ const tool = withToolBillingUnit(createToolUseCases({
205
249
  catalog: modelCatalog,
250
+ tools: product.tools,
206
251
  execution: createToolApiAdapter({ transport: toolTransport, telemetry }),
207
252
  uploader: transientUploader,
208
253
  warn: (message) => { presenter.warning(message); },
209
254
  telemetry
210
- });
255
+ }), product.billingUnit);
211
256
  const editor = createEditorUseCases({
212
257
  session: createEditorSession({
213
258
  store: createEditorSessionStateStore({
214
259
  ...(options.homeDirectory === undefined
215
260
  ? {}
216
- : { homeDirectory: options.homeDirectory })
261
+ : { homeDirectory: options.homeDirectory }),
262
+ stateDirectory: product.stateDirectory
217
263
  }),
218
264
  browser,
219
- defaultCreateUrl: new URL("https://www.gaoding.art/editor/canvas?mode=create&type=board"),
220
- workUrl: new URL("https://www.gaoding.art/editor/canvas"),
265
+ defaultCreateUrl: product.defaultEditorCreateUrl,
266
+ workUrl: product.editorWorkUrl,
267
+ commandName: product.commandName,
268
+ restrictTargets: product.restrictEditorTargets,
269
+ stateDirectory: product.stateDirectory,
221
270
  ...(options.homeDirectory === undefined
222
271
  ? {}
223
272
  : { homeDirectory: options.homeDirectory })
@@ -233,13 +282,18 @@ export function createProductionRuntime(options = {}) {
233
282
  });
234
283
  const update = {
235
284
  run(signal) {
236
- const packageRoot = dirname(dirname(primarySkill.sourceDirectory));
237
285
  return createUpdateService({
238
286
  currentVersion: primarySkill.version,
239
287
  packageRoot,
240
288
  bundledSkills,
241
289
  fetch,
242
290
  telemetry,
291
+ ...(product.id === "insmind" ? {
292
+ packageName: product.npmPackage,
293
+ commandName: product.commandName,
294
+ registryUrl: `https://registry.npmjs.org/${encodeURIComponent(product.npmPackage)}`
295
+ } : {}),
296
+ installationOwner,
243
297
  ...(process.argv[1] ? { binaryPath: process.argv[1] } : {}),
244
298
  ...(options.homeDirectory ? { homeDirectory: options.homeDirectory } : {})
245
299
  }).run(signal);
@@ -249,11 +303,18 @@ export function createProductionRuntime(options = {}) {
249
303
  currentVersion: primarySkill.version,
250
304
  fetch,
251
305
  now,
306
+ installationOwner,
252
307
  ...(options.homeDirectory ? { homeDirectory: options.homeDirectory } : {}),
253
308
  ...(options.env ? { env: options.env } : {}),
254
- notify: (message) => { presenter.notice(message); }
309
+ notify: (message) => { presenter.notice(message); },
310
+ ...(product.id === "insmind" ? {
311
+ stateDirectoryName: product.stateDirectory,
312
+ registryUrl: `https://registry.npmjs.org/${encodeURIComponent(product.npmPackage)}`,
313
+ commandName: product.commandName
314
+ } : {})
255
315
  });
256
316
  return {
317
+ product,
257
318
  version: primarySkill.version,
258
319
  telemetry,
259
320
  telemetrySink,
@@ -268,6 +329,7 @@ export function createProductionRuntime(options = {}) {
268
329
  editor,
269
330
  dam,
270
331
  tool,
332
+ credits,
271
333
  update,
272
334
  updateNotification,
273
335
  jsonInput: createJsonInputReader({
@@ -35,8 +35,6 @@ const editorDisconnectOutputSchema = contractSchema("editor.disconnect", "output
35
35
  const editorSaveOutputSchema = contractSchema("editor.save", "output");
36
36
  const editorScreenshotOutputSchema = contractSchema("editor.screenshot", "output");
37
37
  const editorSnapshotOutputSchema = contractSchema("editor.snapshot", "output");
38
- const orgCurrentSchema = contractSchema("org.current", "output");
39
- const orgListSchema = contractSchema("org.list", "output");
40
38
  const modelGetInputSchema = contractSchema("model.get", "input");
41
39
  const modelGetOutputSchema = contractSchema("model.get", "output");
42
40
  const modelListInputSchema = contractSchema("model.list", "input");
@@ -56,7 +54,7 @@ export class OutputContractError extends Error {
56
54
  this.name = "OutputContractError";
57
55
  }
58
56
  }
59
- export function createContractValidators() {
57
+ export function createContractValidators(options = {}) {
60
58
  const ajv = new Ajv2020({ allErrors: true, strict: true });
61
59
  const addFormats = addFormatsImport;
62
60
  addFormats(ajv);
@@ -94,8 +92,13 @@ export function createContractValidators() {
94
92
  const editorApplyOutput = ajv.compile(editorApplyOutputSchema);
95
93
  const editorScreenshotOutput = ajv.compile(editorScreenshotOutputSchema);
96
94
  const editorSaveOutput = ajv.compile(editorSaveOutputSchema);
97
- const orgList = ajv.compile(orgListSchema);
98
- const orgCurrent = ajv.compile(orgCurrentSchema);
95
+ const organizations = options.organizations !== false;
96
+ const orgList = organizations
97
+ ? ajv.compile(contractSchema("org.list", "output"))
98
+ : unavailableValidator;
99
+ const orgCurrent = organizations
100
+ ? ajv.compile(contractSchema("org.current", "output"))
101
+ : unavailableValidator;
99
102
  const toolListOutput = ajv.compile(toolListOutputSchema);
100
103
  const modelListInput = ajv.compile(modelListInputSchema);
101
104
  const modelListOutput = ajv.compile(modelListOutputSchema);
@@ -149,6 +152,7 @@ export function createContractValidators() {
149
152
  toolCallOutput: assertion(toolCallOutput, OutputContractError)
150
153
  };
151
154
  }
155
+ const unavailableValidator = Object.assign(() => false, { errors: null, schema: false, schemaEnv: undefined });
152
156
  function assertion(validate, ErrorType) {
153
157
  return (value) => {
154
158
  if (!validate(value))
@@ -1,15 +1,16 @@
1
1
  import { contractSchema } from "../contracts/schema.js";
2
2
  import { bindPreparedAction } from "./action-binding.js";
3
3
  import { CliUsageError } from "./errors.js";
4
+ import { productText as text } from "../product/text.js";
4
5
  const agentSendInputSchema = contractSchema("agent.send", "input");
5
6
  export function registerAgentCommands(program, runtime) {
6
7
  const agent = program.command("agent")
7
- .description("稿定 Agent")
8
+ .description(text(runtime.product, "稿定 Agent", "insMind Agent"))
8
9
  .helpCommand(false);
9
10
  const send = agent.command("send")
10
- .description("发送结构化创作消息")
11
- .option("--schema", "输出输入 Schema")
12
- .option("--input <file|->", "从文件或标准输入读取 JSON")
11
+ .description(text(runtime.product, "发送结构化创作消息", "Send a structured creation message"))
12
+ .option("--schema", text(runtime.product, "输出输入 Schema", "output the input Schema"))
13
+ .option("--input <file|->", text(runtime.product, "从文件或标准输入读取 JSON", "read JSON from a file or stdin"))
13
14
  .allowExcessArguments(false);
14
15
  bindPreparedAction(send, "organization", async (signal) => {
15
16
  signal.throwIfAborted();
@@ -1,12 +1,13 @@
1
1
  import { bindAction } from "./action-binding.js";
2
- import { authStatusView } from "./presenter.js";
2
+ import { authStatusViewFor } from "./presenter.js";
3
+ import { productText as text } from "../product/text.js";
3
4
  export function registerAuthCommands(program, runtime) {
4
5
  const auth = program.command("auth")
5
- .description("登录与本机凭证")
6
+ .description(text(runtime.product, "登录与本机凭证", "Authentication and local credentials"))
6
7
  .helpCommand(false);
7
8
  const login = leaf(auth.command("login"))
8
- .description("登录稿定")
9
- .option("--no-browser", "不自动打开浏览器");
9
+ .description(text(runtime.product, "登录稿定", "Sign in to insMind"))
10
+ .option("--no-browser", text(runtime.product, "不自动打开浏览器", "do not open a browser automatically"));
10
11
  bindAction(login, "none", async (_context, signal) => {
11
12
  const noBrowser = login.opts().browser === false;
12
13
  runtime.telemetry.annotate({ parameters: { noBrowser } });
@@ -23,30 +24,32 @@ export function registerAuthCommands(program, runtime) {
23
24
  }
24
25
  });
25
26
  await runtime.telemetry.stage("output", async () => {
26
- runtime.presenter.success(result.status === "already_logged_in" ? "已登录。" : "登录成功。");
27
+ runtime.presenter.success(result.status === "already_logged_in"
28
+ ? text(runtime.product, "已登录。", "Already signed in.")
29
+ : text(runtime.product, "登录成功。", "Signed in successfully."));
27
30
  if (result.status === "already_logged_in" && !result.organizationSelected) {
28
31
  runtime.presenter.warning("尚未选择组织,请执行 gd-cli org switch。");
29
32
  }
30
33
  });
31
34
  }, { operation: "auth.login" });
32
35
  const status = leaf(auth.command("status"))
33
- .description("查看登录状态")
34
- .option("--json", "输出 JSON");
36
+ .description(text(runtime.product, "查看登录状态", "Show authentication status"))
37
+ .option("--json", text(runtime.product, "输出 JSON", "output JSON"));
35
38
  bindAction(status, "none", async () => {
36
39
  const result = await runtime.auth.status({ now: runtime.now() });
37
40
  await runtime.telemetry.stage("output", async () => {
38
41
  runtime.validators.authStatus(result);
39
42
  runtime.presenter.result(result, {
40
43
  json: status.opts().json === true,
41
- view: authStatusView
44
+ view: authStatusViewFor(runtime.product)
42
45
  });
43
46
  });
44
47
  }, { operation: "auth.status" });
45
- const logout = leaf(auth.command("logout")).description("退出登录");
48
+ const logout = leaf(auth.command("logout")).description(text(runtime.product, "退出登录", "Sign out"));
46
49
  bindAction(logout, "none", async () => {
47
50
  await runtime.auth.logout();
48
51
  await runtime.telemetry.stage("output", async () => {
49
- runtime.presenter.success("已退出登录。");
52
+ runtime.presenter.success(text(runtime.product, "已退出登录。", "Signed out."));
50
53
  });
51
54
  }, { operation: "auth.logout" });
52
55
  }
@@ -0,0 +1,23 @@
1
+ import { bindAction } from "./action-binding.js";
2
+ export function registerCreditsCommand(program, runtime) {
3
+ const credits = program.command("credits")
4
+ .description("Show the current insMind credits balance")
5
+ .option("--json", "output JSON")
6
+ .allowExcessArguments(false);
7
+ bindAction(credits, "organization", async (context, signal) => {
8
+ const result = await runtime.credits.get({
9
+ credential: context.state.credential,
10
+ organizationId: context.state.organization.id,
11
+ signal
12
+ });
13
+ await runtime.telemetry.stage("output", async () => {
14
+ runtime.presenter.result(result, {
15
+ json: credits.opts().json === true,
16
+ view: (value) => {
17
+ const balance = value;
18
+ return `${balance.credits.available} insMind credits available`;
19
+ }
20
+ });
21
+ });
22
+ }, { operation: "credits.get" });
23
+ }