@ogment-ai/cli 0.4.2 → 0.5.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 (71) hide show
  1. package/dist/cli/commands.d.ts +38 -0
  2. package/dist/cli/commands.d.ts.map +1 -0
  3. package/dist/cli/commands.js +57 -0
  4. package/dist/cli/execute.d.ts +11 -0
  5. package/dist/cli/execute.d.ts.map +1 -0
  6. package/dist/cli/execute.js +487 -0
  7. package/dist/cli/invocations.d.ts +32 -0
  8. package/dist/cli/invocations.d.ts.map +1 -0
  9. package/dist/cli/invocations.js +1 -0
  10. package/dist/cli/parse-errors.d.ts +17 -0
  11. package/dist/cli/parse-errors.d.ts.map +1 -0
  12. package/dist/cli/parse-errors.js +184 -0
  13. package/dist/cli/program.d.ts +10 -0
  14. package/dist/cli/program.d.ts.map +1 -0
  15. package/dist/cli/program.js +183 -0
  16. package/dist/cli/run.d.ts +6 -0
  17. package/dist/cli/run.d.ts.map +1 -0
  18. package/dist/cli/run.js +83 -0
  19. package/dist/cli/runtime.d.ts +22 -0
  20. package/dist/cli/runtime.d.ts.map +1 -0
  21. package/dist/cli/runtime.js +86 -0
  22. package/dist/cli.d.ts +2 -20
  23. package/dist/cli.d.ts.map +1 -1
  24. package/dist/cli.js +2 -737
  25. package/dist/commands/catalog.d.ts +3 -1
  26. package/dist/commands/catalog.d.ts.map +1 -1
  27. package/dist/commands/catalog.js +19 -2
  28. package/dist/commands/invoke.d.ts.map +1 -1
  29. package/dist/commands/invoke.js +53 -3
  30. package/dist/infra/http.d.ts +5 -1
  31. package/dist/infra/http.d.ts.map +1 -1
  32. package/dist/infra/http.js +62 -5
  33. package/dist/output/envelope.d.ts +5 -2
  34. package/dist/output/envelope.d.ts.map +1 -1
  35. package/dist/output/envelope.js +39 -23
  36. package/dist/output/manager.d.ts +9 -3
  37. package/dist/output/manager.d.ts.map +1 -1
  38. package/dist/output/manager.js +53 -2
  39. package/dist/services/account.d.ts.map +1 -1
  40. package/dist/services/account.js +9 -16
  41. package/dist/services/auth.d.ts.map +1 -1
  42. package/dist/services/auth.js +70 -52
  43. package/dist/services/info.d.ts.map +1 -1
  44. package/dist/services/info.js +62 -0
  45. package/dist/services/mcp-error-mapping.d.ts +9 -0
  46. package/dist/services/mcp-error-mapping.d.ts.map +1 -0
  47. package/dist/services/mcp-error-mapping.js +129 -0
  48. package/dist/services/mcp.d.ts +8 -2
  49. package/dist/services/mcp.d.ts.map +1 -1
  50. package/dist/services/mcp.js +24 -14
  51. package/dist/shared/error-codes.d.ts +4 -1
  52. package/dist/shared/error-codes.d.ts.map +1 -1
  53. package/dist/shared/error-presentation.d.ts +17 -0
  54. package/dist/shared/error-presentation.d.ts.map +1 -0
  55. package/dist/shared/error-presentation.js +151 -0
  56. package/dist/shared/errors.d.ts +34 -14
  57. package/dist/shared/errors.d.ts.map +1 -1
  58. package/dist/shared/errors.js +126 -25
  59. package/dist/shared/guards.d.ts +2 -1
  60. package/dist/shared/guards.d.ts.map +1 -1
  61. package/dist/shared/guards.js +1 -3
  62. package/dist/shared/recovery.d.ts +5 -0
  63. package/dist/shared/recovery.d.ts.map +1 -0
  64. package/dist/shared/recovery.js +123 -0
  65. package/dist/shared/types.d.ts +53 -13
  66. package/dist/shared/types.d.ts.map +1 -1
  67. package/dist/shared/types.js +1 -1
  68. package/package.json +2 -2
  69. package/dist/shared/retry.d.ts +0 -17
  70. package/dist/shared/retry.d.ts.map +0 -1
  71. package/dist/shared/retry.js +0 -27
package/dist/cli.js CHANGED
@@ -1,741 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { realpathSync } from "node:fs";
3
- import { fileURLToPath, pathToFileURL } from "node:url";
4
- import { Command, CommanderError } from "commander";
5
- import { runAuthLoginCommand, runAuthLogoutCommand, runAuthStatusCommand, } from "./commands/auth.js";
6
- import { runCatalogCommand, runCatalogToolDetailsCommand, runCatalogToolsCommand, } from "./commands/catalog.js";
7
- import { runInvokeCommand } from "./commands/invoke.js";
8
- import { runStatusCommand } from "./commands/status.js";
9
- import { createBrowserOpener } from "./infra/browser.js";
10
- import { createFileCredentialsStore } from "./infra/credentials.js";
11
- import { createRuntimeConfig } from "./infra/env.js";
12
- import { createHttpClient } from "./infra/http.js";
13
- import { OutputManager } from "./output/manager.js";
14
- import { createAccountService } from "./services/account.js";
15
- import { createAuthService } from "./services/auth.js";
16
- import { createInfoService } from "./services/info.js";
17
- import { createMcpService } from "./services/mcp.js";
18
- import { buildJsonSchemaExample, EXAMPLE_PLACEHOLDER } from "./shared/schema-example.js";
19
- import { ERROR_CODE } from "./shared/error-codes.js";
20
- import { AuthError, ContractMismatchError, NotFoundError, RemoteRequestError, UnexpectedError, ValidationError, } from "./shared/errors.js";
21
- import { EXIT_CODE, exitCodeForError } from "./shared/exit-codes.js";
22
- import { APP_DESCRIPTION, APP_NAME, VERSION } from "./shared/constants.js";
23
- class CliExitError extends Error {
24
- exitCode;
25
- constructor(exitCode) {
26
- super("CLI exit");
27
- this.exitCode = exitCode;
28
- }
29
- }
30
- const createRuntime = () => {
31
- const runtimeConfig = createRuntimeConfig();
32
- const output = new OutputManager();
33
- const credentialsStore = createFileCredentialsStore({
34
- configDir: runtimeConfig.configDir,
35
- credentialsPath: runtimeConfig.credentialsPath,
36
- });
37
- const httpClient = createHttpClient();
38
- const browserOpener = createBrowserOpener();
39
- const services = {
40
- account: createAccountService({
41
- baseUrl: runtimeConfig.baseUrl,
42
- httpClient,
43
- }),
44
- auth: createAuthService({
45
- baseUrl: runtimeConfig.baseUrl,
46
- browserOpener,
47
- credentialsStore,
48
- envApiKey: runtimeConfig.envApiKey,
49
- httpClient,
50
- }),
51
- mcp: createMcpService({
52
- baseUrl: runtimeConfig.baseUrl,
53
- version: runtimeConfig.version,
54
- }),
55
- };
56
- const infoService = createInfoService({
57
- accountService: services.account,
58
- baseUrl: runtimeConfig.baseUrl,
59
- baseUrlSource: runtimeConfig.baseUrlSource,
60
- configDir: runtimeConfig.configDir,
61
- credentialsPath: runtimeConfig.credentialsPath,
62
- credentialsStore,
63
- envApiKey: runtimeConfig.envApiKey,
64
- httpClient,
65
- version: runtimeConfig.version,
66
- });
67
- return {
68
- context: {
69
- apiKeyOverride: undefined,
70
- output,
71
- services,
72
- },
73
- infoService,
74
- output,
75
- };
76
- };
77
- const asGlobalOptions = (command) => {
78
- const options = command.optsWithGlobals();
79
- return {
80
- apiKey: options.apiKey,
81
- human: options.human,
82
- nonInteractive: options.nonInteractive,
83
- quiet: options.quiet,
84
- yes: options.yes,
85
- };
86
- };
87
- const mapGlobalOutputOptions = (options) => {
88
- return {
89
- human: options.human,
90
- nonInteractive: options.nonInteractive,
91
- quiet: options.quiet,
92
- yes: options.yes,
93
- };
94
- };
95
- const throwCommandError = (error) => {
96
- throw new CliExitError(exitCodeForError(error));
97
- };
98
- const nextAction = (id, title, command, reason, when = null) => {
99
- return {
100
- command,
101
- id,
102
- reason,
103
- title,
104
- when,
105
- };
106
- };
107
- const hasPlaceholderTokens = (command) => {
108
- return command.includes("<") || command.includes(">");
109
- };
110
- const nextActionsForError = (error) => {
111
- const fallbackByCode = {
112
- [ERROR_CODE.authDeviceExpired]: {
113
- command: "ogment auth login",
114
- reason: "Device code expired; restart login to continue.",
115
- title: "Restart login",
116
- },
117
- [ERROR_CODE.authDevicePending]: {
118
- command: "ogment auth status",
119
- reason: "Authorization is pending; check whether credentials are now available.",
120
- title: "Check auth status",
121
- },
122
- [ERROR_CODE.authInvalidCredentials]: {
123
- command: "ogment auth logout",
124
- reason: "Credentials are invalid; clear local state before re-authenticating.",
125
- title: "Reset auth state",
126
- },
127
- [ERROR_CODE.authRequired]: {
128
- command: "ogment auth login",
129
- reason: "Authentication is required before catalog or invoke commands can run.",
130
- title: "Authenticate",
131
- },
132
- [ERROR_CODE.remoteRateLimited]: {
133
- command: "ogment status",
134
- reason: "Remote rate limit detected; check diagnostics before retrying.",
135
- title: "Inspect diagnostics",
136
- },
137
- [ERROR_CODE.remoteUnavailable]: {
138
- command: "ogment status",
139
- reason: "Remote service is unavailable; check connectivity and endpoint health.",
140
- title: "Inspect diagnostics",
141
- },
142
- [ERROR_CODE.toolInputSchemaViolation]: {
143
- command: "ogment catalog",
144
- reason: "Tool contract did not validate; inspect available servers and tool metadata.",
145
- title: "Inspect catalog",
146
- },
147
- [ERROR_CODE.toolNotFound]: {
148
- command: "ogment catalog",
149
- reason: "Requested tool could not be found; rediscover servers and tools.",
150
- title: "Rediscover tools",
151
- },
152
- [ERROR_CODE.transportRequestFailed]: {
153
- command: "ogment status",
154
- reason: "Transport request failed; inspect connectivity diagnostics.",
155
- title: "Inspect diagnostics",
156
- },
157
- [ERROR_CODE.contractVersionUnsupported]: {
158
- command: "ogment --version",
159
- reason: "Contract mismatch detected; verify CLI version and upgrade before retrying.",
160
- title: "Inspect CLI version",
161
- },
162
- [ERROR_CODE.internalUnexpected]: {
163
- command: "ogment status",
164
- reason: "Unexpected failure detected; run diagnostics before retrying the workflow.",
165
- title: "Inspect diagnostics",
166
- },
167
- [ERROR_CODE.validationInvalidInput]: {
168
- command: "ogment --help",
169
- reason: "Input validation failed; inspect canonical command syntax.",
170
- title: "Show command help",
171
- },
172
- };
173
- const fallback = fallbackByCode[error.code];
174
- const suggestedCommand = typeof error.suggestedCommand === "string" && !hasPlaceholderTokens(error.suggestedCommand)
175
- ? error.suggestedCommand
176
- : undefined;
177
- const command = suggestedCommand ?? fallback?.command;
178
- if (command === undefined) {
179
- return [];
180
- }
181
- return [
182
- nextAction(`recover_${error.code.toLowerCase()}`, fallback?.title ?? "Run suggested command", command, fallback?.reason ?? `Suggested recovery for error ${error.code}.`, "immediate"),
183
- ];
184
- };
185
- const ensureSuccess = (result, output, context) => {
186
- if (result.status === "error") {
187
- output.error(result.error, {
188
- command: context.command,
189
- entity: context.entity ?? null,
190
- nextActions: nextActionsForError(result.error),
191
- });
192
- throwCommandError(result.error);
193
- }
194
- return result.value;
195
- };
196
- const nextActionsForLogin = (payload, mode) => {
197
- return [
198
- nextAction("check_auth_status", "Check auth status", "ogment auth status", `Verify persisted credentials after ${mode} login for ${payload.agentName}.`, "immediate"),
199
- nextAction("discover_servers", "Discover servers", "ogment catalog", `Logged in as ${payload.agentName} via ${mode}; discover available servers.`, "after_auth"),
200
- ];
201
- };
202
- const nextActionsForAuthStatus = (payload) => {
203
- if (!payload.loggedIn) {
204
- return [
205
- nextAction("login", "Authenticate", "ogment auth login", "No API key is configured locally; login is required.", "immediate"),
206
- ];
207
- }
208
- return [
209
- nextAction("discover_servers", "Discover servers", "ogment catalog", `Authenticated via ${payload.apiKeySource}; discover servers next.`, "after_auth"),
210
- ];
211
- };
212
- const nextActionsForCatalogSummary = (payload, context) => {
213
- const actions = [];
214
- const targetServer = payload.servers.find((server) => server.toolCount > 0);
215
- if (targetServer !== undefined) {
216
- actions.push(nextAction("inspect_tools", "Inspect tools", `ogment catalog ${targetServer.serverId}`, `${targetServer.serverId} has ${targetServer.toolCount} available tools.`, "if_tool_count_gt_0"));
217
- }
218
- if (context.nextCursor !== null) {
219
- const limitFlag = context.limit === null ? "" : ` --limit ${context.limit}`;
220
- actions.push(nextAction("next_catalog_page", "Load next page", `ogment catalog --cursor ${context.nextCursor}${limitFlag}`, `More servers are available after cursor ${context.nextCursor}.`, "if_more_servers"));
221
- }
222
- return actions;
223
- };
224
- const nextActionsForCatalogTools = (payload) => {
225
- const firstTool = payload.tools[0];
226
- if (firstTool === undefined) {
227
- return [];
228
- }
229
- return [
230
- nextAction("inspect_tool", "Inspect tool details", `ogment catalog ${payload.server.serverId} ${firstTool.name}`, `Inspect schema for ${payload.server.serverId}/${firstTool.name} before invoking.`, "if_tools_available"),
231
- ];
232
- };
233
- const toInlineJsonTemplateArgument = (value) => {
234
- const placeholderLiteral = JSON.stringify(EXAMPLE_PLACEHOLDER);
235
- const serialized = JSON.stringify(value).replaceAll(placeholderLiteral, " ... ");
236
- const escaped = serialized.replaceAll("'", String.raw `'\''`);
237
- return `'${escaped}'`;
238
- };
239
- const nextActionsForCatalogToolDetails = (payload, exampleInput) => {
240
- const inputArgument = toInlineJsonTemplateArgument(exampleInput);
241
- return [
242
- nextAction("invoke_tool", "Invoke this tool", `ogment invoke ${payload.server.serverId}/${payload.name} --input ${inputArgument}`, `Replace every ... placeholder with real JSON values, then invoke ${payload.server.serverId}/${payload.name}.`, "after_tool_inspection"),
243
- ];
244
- };
245
- const nextActionsForInvoke = (payload) => {
246
- return [
247
- nextAction("inspect_tool", "Inspect tool schema", `ogment catalog ${payload.serverId} ${payload.toolName}`, `Review ${payload.serverId}/${payload.toolName} schema for the next invocation.`, "after_invoke"),
248
- ];
249
- };
250
- const nextActionsForStatus = (payload) => {
251
- if (!payload.auth.apiKeyPresent) {
252
- return [
253
- nextAction("login", "Authenticate", "ogment auth login", "Status detected no API key; authenticate first.", "immediate"),
254
- ];
255
- }
256
- return [
257
- nextAction("discover_servers", "Discover servers", "ogment catalog", `Connectivity is ${payload.summary.status}; discover available servers.`, "after_status"),
258
- ];
259
- };
260
- const createProgram = (runtime) => {
261
- const program = new Command();
262
- program.exitOverride();
263
- program.configureOutput({
264
- writeErr: () => undefined,
265
- });
266
- program
267
- .name(APP_NAME)
268
- .description(APP_DESCRIPTION)
269
- .version(VERSION)
270
- .option("--api-key <key>", "API key override")
271
- .option("--human", "Render output for humans")
272
- .option("--quiet", "Suppress non-essential output")
273
- .option("--non-interactive", "Disable interactive behavior")
274
- .option("--yes", "Assume yes for any confirmation")
275
- .hook("preAction", (thisCommand) => {
276
- const options = asGlobalOptions(thisCommand);
277
- runtime.output.configure(mapGlobalOutputOptions(options));
278
- runtime.context.apiKeyOverride = options.apiKey;
279
- });
280
- const authCommand = program.command("auth").description("Authentication workflows");
281
- const runLoginFlow = async (mode, apiKey) => {
282
- const loginInvocation = (() => {
283
- if (mode === "api_key") {
284
- return {
285
- commandOptions: {
286
- apiKey: apiKey ?? "",
287
- mode: "apiKey",
288
- },
289
- invokedCommand: "ogment auth login --api-key <redacted>",
290
- };
291
- }
292
- if (mode === "browser") {
293
- return {
294
- commandOptions: {
295
- mode: "browser",
296
- },
297
- invokedCommand: "ogment auth login --browser",
298
- };
299
- }
300
- const deviceCommand = "ogment auth login";
301
- return {
302
- commandOptions: {
303
- mode: "device",
304
- onPending: ({ userCode, verificationUri }) => {
305
- const pendingNextActions = [
306
- nextAction("check_login_status", "Check login status", "ogment auth status", `Approve ${userCode} at ${verificationUri}, then verify local auth state.`, "immediate"),
307
- nextAction("restart_device_login", "Restart login", "ogment auth login", `Restart login if code ${userCode} expires before approval.`, "if_expired"),
308
- ];
309
- runtime.output.success({
310
- state: "pending",
311
- userCode,
312
- verificationUri,
313
- }, {
314
- command: deviceCommand,
315
- entity: {
316
- mode,
317
- state: "pending",
318
- userCode,
319
- verificationUri,
320
- },
321
- humanMessage: `Open ${verificationUri} and enter code ${userCode}`,
322
- nextActions: pendingNextActions,
323
- });
324
- },
325
- },
326
- invokedCommand: deviceCommand,
327
- };
328
- })();
329
- const { commandOptions, invokedCommand } = loginInvocation;
330
- const result = await runAuthLoginCommand(runtime.context, commandOptions);
331
- const data = ensureSuccess(result, runtime.output, {
332
- command: invokedCommand,
333
- entity: {
334
- mode,
335
- },
336
- });
337
- let humanMessage = `Logged in as ${data.agentName}.`;
338
- if (data.alreadyLoggedIn) {
339
- humanMessage = `Already logged in as ${data.agentName}.`;
340
- }
341
- else if (mode === "api_key") {
342
- humanMessage = `Imported API key for ${data.agentName}.`;
343
- }
344
- runtime.output.success(data, {
345
- command: invokedCommand,
346
- entity: {
347
- agentName: data.agentName,
348
- alreadyLoggedIn: data.alreadyLoggedIn,
349
- mode,
350
- },
351
- humanMessage,
352
- nextActions: nextActionsForLogin(data, mode),
353
- });
354
- };
355
- const loginWorkflowCommand = authCommand
356
- .command("login")
357
- .description("Authenticate with Ogment using device flow (recommended)")
358
- .option("--browser", "Fallback: use browser callback flow");
359
- loginWorkflowCommand.action(async (options, command) => {
360
- const { apiKey } = asGlobalOptions(command);
361
- if (options.browser === true && apiKey !== undefined) {
362
- throw new ValidationError({
363
- details: "--browser with --api-key",
364
- message: "Use only one login mode: default device, --browser, or --api-key.",
365
- suggestedCommand: "ogment auth login",
366
- });
367
- }
368
- if (apiKey !== undefined) {
369
- await runLoginFlow("api_key", apiKey);
370
- return;
371
- }
372
- if (options.browser === true) {
373
- await runLoginFlow("browser");
374
- return;
375
- }
376
- await runLoginFlow("device");
377
- });
378
- authCommand
379
- .command("status")
380
- .description("Show local authentication status")
381
- .action(async () => {
382
- const command = "ogment auth status";
383
- const result = await runAuthStatusCommand(runtime.context);
384
- const data = ensureSuccess(result, runtime.output, {
385
- command,
386
- });
387
- runtime.output.success(data, {
388
- command,
389
- entity: {
390
- apiKeySource: data.apiKeySource,
391
- loggedIn: data.loggedIn,
392
- },
393
- humanMessage: data.loggedIn
394
- ? `Authenticated (${data.apiKeySource}).`
395
- : "Not authenticated.",
396
- nextActions: nextActionsForAuthStatus(data),
397
- });
398
- });
399
- authCommand
400
- .command("logout")
401
- .description("Revoke token and delete local credentials")
402
- .action(async () => {
403
- const command = "ogment auth logout";
404
- const result = await runAuthLogoutCommand(runtime.context);
405
- const data = ensureSuccess(result, runtime.output, {
406
- command,
407
- });
408
- let message = "Not logged in.";
409
- if (data.localCredentialsDeleted) {
410
- message = data.revoked
411
- ? "Logged out and revoked API key."
412
- : "Logged out locally. Server revocation not confirmed.";
413
- }
414
- runtime.output.success(data, {
415
- command,
416
- entity: {
417
- localCredentialsDeleted: data.localCredentialsDeleted,
418
- revoked: data.revoked,
419
- },
420
- humanMessage: message,
421
- nextActions: [
422
- nextAction("login", "Authenticate again", "ogment auth login", "Logout completed; authenticate again before further tool calls.", "after_logout"),
423
- ],
424
- });
425
- });
426
- const catalogCommand = program
427
- .command("catalog [serverId] [toolName]")
428
- .description("Discover servers and tools with progressive disclosure")
429
- .option("--cursor <cursor>", "Pagination cursor (catalog summary only)")
430
- .option("--limit <limit>", "Maximum servers to return (catalog summary only)")
431
- .option("--example", "Include a generated example input payload (tool details only)");
432
- catalogCommand.action(async (serverId, toolName, options) => {
433
- const parsedLimit = typeof options.limit === "string" ? Number.parseInt(options.limit, 10) : undefined;
434
- if (options.limit !== undefined && (parsedLimit === undefined || Number.isNaN(parsedLimit))) {
435
- throw new ValidationError({
436
- details: options.limit,
437
- message: "Invalid --limit value. Expected an integer.",
438
- suggestedCommand: "ogment catalog --limit 20",
439
- });
440
- }
441
- if (parsedLimit !== undefined && parsedLimit < 1) {
442
- throw new ValidationError({
443
- details: options.limit ?? String(parsedLimit),
444
- message: "Invalid --limit value. Expected a positive integer.",
445
- suggestedCommand: "ogment catalog --limit 20",
446
- });
447
- }
448
- if (serverId === undefined) {
449
- if (options.example === true) {
450
- throw new ValidationError({
451
- details: "--example without <server-id> <tool-name>",
452
- message: "Invalid catalog options. --example requires <server-id> <tool-name>.",
453
- suggestedCommand: "ogment catalog",
454
- });
455
- }
456
- const command = [
457
- "ogment catalog",
458
- ...(options.cursor === undefined ? [] : [`--cursor ${options.cursor}`]),
459
- ...(parsedLimit === undefined || Number.isNaN(parsedLimit)
460
- ? []
461
- : [`--limit ${parsedLimit}`]),
462
- ].join(" ");
463
- const result = await runCatalogCommand(runtime.context, {
464
- cursor: options.cursor,
465
- limit: parsedLimit,
466
- });
467
- const data = ensureSuccess(result, runtime.output, {
468
- command,
469
- entity: {
470
- cursor: options.cursor ?? null,
471
- limit: parsedLimit ?? null,
472
- },
473
- });
474
- const outputData = {
475
- servers: data.servers,
476
- };
477
- runtime.output.success(outputData, {
478
- command,
479
- entity: {
480
- cursor: options.cursor ?? null,
481
- limit: parsedLimit ?? null,
482
- serverCount: data.servers.length,
483
- },
484
- humanMessage: data.nextCursor === null
485
- ? `${data.servers.length} server(s) available.`
486
- : `${data.servers.length} server(s) shown. More available with --cursor ${data.nextCursor}.`,
487
- nextActions: nextActionsForCatalogSummary(outputData, {
488
- limit: parsedLimit ?? null,
489
- nextCursor: data.nextCursor,
490
- }),
491
- pagination: {
492
- nextCursor: data.nextCursor,
493
- },
494
- });
495
- return;
496
- }
497
- if (toolName === undefined) {
498
- if (options.cursor !== undefined || parsedLimit !== undefined) {
499
- throw new ValidationError({
500
- details: `${options.cursor === undefined ? "" : `--cursor ${options.cursor} `}${parsedLimit === undefined ? "" : `--limit ${parsedLimit}`}`.trim(),
501
- message: "Invalid catalog options. --cursor and --limit are only valid for `ogment catalog`.",
502
- suggestedCommand: "ogment catalog",
503
- });
504
- }
505
- if (options.example === true) {
506
- throw new ValidationError({
507
- details: `--example without tool name for ${serverId}`,
508
- message: "Invalid catalog options. --example requires <tool-name>.",
509
- suggestedCommand: `ogment catalog ${serverId}`,
510
- });
511
- }
512
- const command = `ogment catalog ${serverId}`;
513
- const result = await runCatalogToolsCommand(runtime.context, { serverId });
514
- const data = ensureSuccess(result, runtime.output, {
515
- command,
516
- entity: {
517
- serverId,
518
- },
519
- });
520
- runtime.output.success(data, {
521
- command,
522
- entity: {
523
- serverId: data.server.serverId,
524
- toolCount: data.tools.length,
525
- },
526
- humanMessage: `${data.tools.length} tool(s) available on ${data.server.serverId}.`,
527
- nextActions: nextActionsForCatalogTools(data),
528
- });
529
- return;
530
- }
531
- if (options.cursor !== undefined || parsedLimit !== undefined) {
532
- throw new ValidationError({
533
- details: `${options.cursor === undefined ? "" : `--cursor ${options.cursor} `}${parsedLimit === undefined ? "" : `--limit ${parsedLimit}`}`.trim(),
534
- message: "Invalid catalog options. --cursor and --limit are only valid for `ogment catalog`.",
535
- suggestedCommand: `ogment catalog ${serverId} ${toolName}${options.example === true ? " --example" : ""}`,
536
- });
537
- }
538
- const command = [
539
- "ogment catalog",
540
- serverId,
541
- toolName,
542
- ...(options.example === true ? ["--example"] : []),
543
- ].join(" ");
544
- const result = await runCatalogToolDetailsCommand(runtime.context, { serverId, toolName });
545
- const data = ensureSuccess(result, runtime.output, {
546
- command,
547
- entity: {
548
- serverId,
549
- toolName,
550
- },
551
- });
552
- const exampleInput = buildJsonSchemaExample(data.inputSchema);
553
- const outputData = options.example === true ? { ...data, exampleInput } : data;
554
- runtime.output.success(outputData, {
555
- command,
556
- entity: {
557
- serverId: data.server.serverId,
558
- toolName: data.name,
559
- },
560
- humanMessage: `Loaded schema for ${data.server.serverId}/${data.name}.`,
561
- nextActions: nextActionsForCatalogToolDetails(data, exampleInput),
562
- });
563
- });
564
- program
565
- .command("invoke <target>")
566
- .description("Invoke a tool using <server-id>/<tool-name>")
567
- .option("--input <value>", "Input payload: inline JSON object, @path, or - for stdin")
568
- .action(async (target, options) => {
569
- let inputSource = "none";
570
- let command = `ogment invoke ${target}`;
571
- if (options.input !== undefined) {
572
- if (options.input === "-") {
573
- inputSource = "stdin";
574
- command = `ogment invoke ${target} --input -`;
575
- }
576
- else if (options.input.startsWith("@")) {
577
- inputSource = "file";
578
- command = `ogment invoke ${target} --input ${options.input}`;
579
- }
580
- else {
581
- inputSource = "inline_json";
582
- command = `ogment invoke ${target} --input <json>`;
583
- }
584
- }
585
- const result = await runInvokeCommand(runtime.context, {
586
- input: options.input,
587
- target,
588
- });
589
- const data = ensureSuccess(result, runtime.output, {
590
- command,
591
- entity: {
592
- inputSource,
593
- target,
594
- },
595
- });
596
- runtime.output.success(data, {
597
- command,
598
- entity: {
599
- inputSource,
600
- serverId: data.serverId,
601
- toolName: data.toolName,
602
- },
603
- humanMessage: `Invoked ${data.serverId}/${data.toolName}.`,
604
- nextActions: nextActionsForInvoke(data),
605
- });
606
- });
607
- program
608
- .command("status")
609
- .description("Show runtime configuration and connectivity diagnostics")
610
- .action(async () => {
611
- const command = "ogment status";
612
- if (runtime.infoService === undefined) {
613
- throw new UnexpectedError({
614
- message: "Info service is not configured",
615
- });
616
- }
617
- const result = await runStatusCommand({
618
- apiKeyOverride: runtime.context.apiKeyOverride,
619
- }, {
620
- infoService: runtime.infoService,
621
- });
622
- const data = ensureSuccess(result, runtime.output, {
623
- command,
624
- });
625
- runtime.output.success(data, {
626
- command,
627
- entity: {
628
- summaryStatus: data.summary.status,
629
- },
630
- humanMessage: `Diagnostics status: ${data.summary.status.toUpperCase()}`,
631
- nextActions: nextActionsForStatus(data),
632
- });
633
- });
634
- program.action(() => {
635
- runtime.output.success({
636
- commands: [
637
- "auth login",
638
- "auth status",
639
- "auth logout",
640
- "catalog",
641
- "catalog <server-id>",
642
- "catalog <server-id> <tool-name>",
643
- "invoke <server-id>/<tool-name>",
644
- "status",
645
- ],
646
- }, {
647
- command: "ogment",
648
- entity: null,
649
- humanMessage: "Select a command to continue.",
650
- nextActions: [
651
- nextAction("login", "Authenticate", "ogment auth login", "Authenticate first so catalog and invoke commands can run.", "immediate"),
652
- nextAction("catalog", "Discover servers", "ogment catalog", "List accessible servers and tool counts.", "after_auth"),
653
- ],
654
- });
655
- });
656
- return program;
657
- };
658
- const normalizeCommanderError = (error) => {
659
- if (error instanceof AuthError ||
660
- error instanceof ContractMismatchError ||
661
- error instanceof NotFoundError ||
662
- error instanceof RemoteRequestError ||
663
- error instanceof UnexpectedError ||
664
- error instanceof ValidationError) {
665
- return error;
666
- }
667
- if (error instanceof CommanderError) {
668
- return new ValidationError({
669
- details: error.message,
670
- message: "Invalid CLI usage",
671
- });
672
- }
673
- if (error instanceof Error) {
674
- return new UnexpectedError({
675
- cause: error,
676
- message: error.message,
677
- });
678
- }
679
- return new UnexpectedError({
680
- cause: error,
681
- message: "Unexpected CLI failure",
682
- });
683
- };
684
- const normalizeCliArgv = (argv) => {
685
- if (argv[0] === "--") {
686
- return argv.slice(1);
687
- }
688
- return argv;
689
- };
690
- const commandFromArgv = (argv) => {
691
- const normalized = normalizeCliArgv(argv);
692
- if (normalized.length === 0) {
693
- return "ogment";
694
- }
695
- return `ogment ${normalized.join(" ")}`;
696
- };
697
- export const runCli = async (argv = process.argv.slice(2), runtime = createRuntime()) => {
698
- const program = createProgram(runtime);
699
- try {
700
- await program.parseAsync(normalizeCliArgv(argv), { from: "user" });
701
- return EXIT_CODE.success;
702
- }
703
- catch (error) {
704
- if (error instanceof CliExitError) {
705
- return error.exitCode;
706
- }
707
- if (error instanceof CommanderError && error.exitCode === EXIT_CODE.success) {
708
- return EXIT_CODE.success;
709
- }
710
- const normalized = normalizeCommanderError(error);
711
- runtime.output.error(normalized, {
712
- command: commandFromArgv(argv),
713
- entity: null,
714
- nextActions: nextActionsForError(normalized),
715
- });
716
- return exitCodeForError(normalized);
717
- }
718
- };
719
- export const executeCli = async () => {
720
- const code = await runCli();
721
- process.exitCode = code;
722
- };
723
- const toCanonicalFileHref = (path) => {
724
- return pathToFileURL(realpathSync(path)).href;
725
- };
726
- export const shouldExecuteCli = (importMetaUrl, argv1) => {
727
- if (argv1 === undefined) {
728
- return false;
729
- }
730
- try {
731
- const invokedFileHref = toCanonicalFileHref(argv1);
732
- const moduleFileHref = toCanonicalFileHref(fileURLToPath(importMetaUrl));
733
- return invokedFileHref === moduleFileHref;
734
- }
735
- catch {
736
- return false;
737
- }
738
- };
2
+ import { executeCli, runCli, shouldExecuteCli } from "./cli/run.js";
3
+ export { executeCli, runCli, shouldExecuteCli };
739
4
  if (shouldExecuteCli(import.meta.url, process.argv[1])) {
740
5
  await executeCli();
741
6
  }