@ogment-ai/cli 0.3.4 → 0.4.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.
- package/README.md +88 -74
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +512 -247
- package/dist/commands/auth.d.ts +22 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +29 -0
- package/dist/commands/catalog.d.ts +29 -0
- package/dist/commands/catalog.d.ts.map +1 -0
- package/dist/commands/catalog.js +151 -0
- package/dist/commands/invoke.d.ts +17 -0
- package/dist/commands/invoke.d.ts.map +1 -0
- package/dist/commands/invoke.js +123 -0
- package/dist/commands/{info.d.ts → status.d.ts} +4 -4
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/{info.js → status.js} +1 -1
- package/dist/output/envelope.d.ts +19 -0
- package/dist/output/envelope.d.ts.map +1 -0
- package/dist/output/envelope.js +51 -0
- package/dist/output/manager.d.ts +16 -3
- package/dist/output/manager.d.ts.map +1 -1
- package/dist/output/manager.js +27 -29
- package/dist/services/account.d.ts.map +1 -1
- package/dist/services/account.js +18 -2
- package/dist/services/auth.d.ts +14 -3
- package/dist/services/auth.d.ts.map +1 -1
- package/dist/services/auth.js +119 -15
- package/dist/services/info.d.ts.map +1 -1
- package/dist/services/info.js +11 -10
- package/dist/services/mcp.d.ts.map +1 -1
- package/dist/services/mcp.js +24 -23
- package/dist/shared/constants.d.ts +0 -1
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +0 -1
- package/dist/shared/error-codes.d.ts +28 -0
- package/dist/shared/error-codes.d.ts.map +1 -0
- package/dist/shared/error-codes.js +25 -0
- package/dist/shared/errors.d.ts +100 -9
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +103 -0
- package/dist/shared/exit-codes.d.ts +8 -5
- package/dist/shared/exit-codes.d.ts.map +1 -1
- package/dist/shared/exit-codes.js +31 -14
- package/dist/shared/guards.d.ts +5 -1
- package/dist/shared/guards.d.ts.map +1 -1
- package/dist/shared/guards.js +6 -1
- package/dist/shared/retry.d.ts +17 -0
- package/dist/shared/retry.d.ts.map +1 -0
- package/dist/shared/retry.js +27 -0
- package/dist/shared/schema-example.d.ts +2 -0
- package/dist/shared/schema-example.d.ts.map +1 -0
- package/dist/shared/schema-example.js +128 -0
- package/dist/shared/schemas.d.ts +0 -42
- package/dist/shared/schemas.d.ts.map +1 -1
- package/dist/shared/schemas.js +0 -21
- package/dist/shared/types.d.ts +84 -12
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +1 -1
- package/package.json +5 -7
- package/dist/commands/call.d.ts +0 -14
- package/dist/commands/call.d.ts.map +0 -1
- package/dist/commands/call.js +0 -51
- package/dist/commands/describe.d.ts +0 -4
- package/dist/commands/describe.d.ts.map +0 -1
- package/dist/commands/describe.js +0 -109
- package/dist/commands/info.d.ts.map +0 -1
- package/dist/commands/servers.d.ts +0 -13
- package/dist/commands/servers.d.ts.map +0 -1
- package/dist/commands/servers.js +0 -29
- package/dist/postinstall.d.ts +0 -2
- package/dist/postinstall.d.ts.map +0 -1
- package/dist/postinstall.js +0 -12
package/dist/cli.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
import { Command, CommanderError } from "commander";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
9
|
import { createBrowserOpener } from "./infra/browser.js";
|
|
10
10
|
import { createFileCredentialsStore } from "./infra/credentials.js";
|
|
11
11
|
import { createRuntimeConfig } from "./infra/env.js";
|
|
@@ -15,9 +15,11 @@ import { createAccountService } from "./services/account.js";
|
|
|
15
15
|
import { createAuthService } from "./services/auth.js";
|
|
16
16
|
import { createInfoService } from "./services/info.js";
|
|
17
17
|
import { createMcpService } from "./services/mcp.js";
|
|
18
|
-
import {
|
|
18
|
+
import { buildJsonSchemaExample } 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";
|
|
19
21
|
import { EXIT_CODE, exitCodeForError } from "./shared/exit-codes.js";
|
|
20
|
-
import { APP_DESCRIPTION, APP_NAME,
|
|
22
|
+
import { APP_DESCRIPTION, APP_NAME, VERSION } from "./shared/constants.js";
|
|
21
23
|
class CliExitError extends Error {
|
|
22
24
|
exitCode;
|
|
23
25
|
constructor(exitCode) {
|
|
@@ -76,7 +78,7 @@ const asGlobalOptions = (command) => {
|
|
|
76
78
|
const options = command.optsWithGlobals();
|
|
77
79
|
return {
|
|
78
80
|
apiKey: options.apiKey,
|
|
79
|
-
|
|
81
|
+
human: options.human,
|
|
80
82
|
nonInteractive: options.nonInteractive,
|
|
81
83
|
quiet: options.quiet,
|
|
82
84
|
yes: options.yes,
|
|
@@ -84,7 +86,7 @@ const asGlobalOptions = (command) => {
|
|
|
84
86
|
};
|
|
85
87
|
const mapGlobalOutputOptions = (options) => {
|
|
86
88
|
return {
|
|
87
|
-
|
|
89
|
+
human: options.human,
|
|
88
90
|
nonInteractive: options.nonInteractive,
|
|
89
91
|
quiet: options.quiet,
|
|
90
92
|
yes: options.yes,
|
|
@@ -93,177 +95,169 @@ const mapGlobalOutputOptions = (options) => {
|
|
|
93
95
|
const throwCommandError = (error) => {
|
|
94
96
|
throw new CliExitError(exitCodeForError(error));
|
|
95
97
|
};
|
|
96
|
-
const
|
|
97
|
-
return
|
|
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
|
+
];
|
|
98
184
|
};
|
|
99
|
-
const ensureSuccess = (result, output) => {
|
|
185
|
+
const ensureSuccess = (result, output, context) => {
|
|
100
186
|
if (result.status === "error") {
|
|
101
|
-
output.error(result.error
|
|
187
|
+
output.error(result.error, {
|
|
188
|
+
command: context.command,
|
|
189
|
+
entity: context.entity ?? null,
|
|
190
|
+
nextActions: nextActionsForError(result.error),
|
|
191
|
+
});
|
|
102
192
|
throwCommandError(result.error);
|
|
103
193
|
}
|
|
104
194
|
return result.value;
|
|
105
195
|
};
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return `${property.items.type}[]`;
|
|
112
|
-
}
|
|
113
|
-
if (typeof property.type === "string" && property.type.length > 0) {
|
|
114
|
-
return property.type;
|
|
115
|
-
}
|
|
116
|
-
return "unknown";
|
|
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
|
+
];
|
|
117
201
|
};
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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, filters) => {
|
|
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 tools ${targetServer.serverId}`, `${targetServer.serverId} has ${targetServer.toolCount} available tools.`, "if_tool_count_gt_0"));
|
|
132
217
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
output.info(" Parameters: none");
|
|
136
|
-
return;
|
|
218
|
+
if (filters.serverId !== null) {
|
|
219
|
+
actions.push(nextAction("inspect_selected_server_tools", "Inspect selected server tools", `ogment catalog tools ${filters.serverId}`, `Inspect tools on ${filters.serverId} after narrowing catalog output.`, "if_server_selected"));
|
|
137
220
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const property = propertyValue;
|
|
144
|
-
const type = formatSchemaType(property);
|
|
145
|
-
const requirement = required.has(name) ? "required" : "optional";
|
|
146
|
-
const description = typeof property.description === "string" && property.description.length > 0
|
|
147
|
-
? ` - ${property.description}`
|
|
148
|
-
: "";
|
|
149
|
-
output.info(` ${name} (${type}, ${requirement})${description}`);
|
|
221
|
+
if (context.nextCursor !== null) {
|
|
222
|
+
const limitFlag = context.limit === null ? "" : ` --limit ${context.limit}`;
|
|
223
|
+
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"));
|
|
150
224
|
}
|
|
225
|
+
return actions;
|
|
151
226
|
};
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
output.info(` ${line}`);
|
|
161
|
-
}
|
|
227
|
+
const nextActionsForCatalogTools = (payload) => {
|
|
228
|
+
const firstTool = payload.tools[0];
|
|
229
|
+
if (firstTool === undefined) {
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
return [
|
|
233
|
+
nextAction("inspect_tool", "Inspect tool details", `ogment catalog tool ${payload.server.serverId} ${firstTool.name}`, `Inspect schema for ${payload.server.serverId}/${firstTool.name} before invoking.`, "if_tools_available"),
|
|
234
|
+
];
|
|
162
235
|
};
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
for (const server of servers) {
|
|
169
|
-
const description = typeof server.description === "string" && server.description.length > 0
|
|
170
|
-
? ` - ${server.description}`
|
|
171
|
-
: "";
|
|
172
|
-
output.info(`${server.path} (${server.orgSlug}) ${server.name}${description}`);
|
|
173
|
-
}
|
|
174
|
-
output.info("Inspect tools with: ogment servers <path>");
|
|
236
|
+
const toInlineJsonArgument = (value) => {
|
|
237
|
+
const serialized = JSON.stringify(value);
|
|
238
|
+
const escaped = serialized.replaceAll("'", String.raw `'\''`);
|
|
239
|
+
return `'${escaped}'`;
|
|
175
240
|
};
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
for (const tool of payload.tools) {
|
|
183
|
-
const description = typeof tool.description === "string" && tool.description.length > 0
|
|
184
|
-
? ` - ${tool.description}`
|
|
185
|
-
: "";
|
|
186
|
-
output.info(` ${tool.name}${description}`);
|
|
187
|
-
renderSchemaParameters(output, tool.inputSchema);
|
|
188
|
-
renderSchemaBlock(output, "Input schema", tool.inputSchema);
|
|
189
|
-
renderSchemaBlock(output, "Output schema", tool.outputSchema);
|
|
190
|
-
}
|
|
241
|
+
const nextActionsForCatalogToolDetails = (payload, exampleInput) => {
|
|
242
|
+
const inputArgument = toInlineJsonArgument(exampleInput);
|
|
243
|
+
return [
|
|
244
|
+
nextAction("invoke_tool", "Invoke this tool", `ogment invoke ${payload.server.serverId}/${payload.name} --input ${inputArgument}`, `Invoke ${payload.server.serverId}/${payload.name} with schema-shaped input.`, "after_tool_inspection"),
|
|
245
|
+
];
|
|
191
246
|
};
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return `${latencyMs}ms`;
|
|
247
|
+
const nextActionsForInvoke = (payload) => {
|
|
248
|
+
return [
|
|
249
|
+
nextAction("inspect_tool", "Inspect tool schema", `ogment catalog tool ${payload.serverId} ${payload.toolName}`, `Review ${payload.serverId}/${payload.toolName} schema for the next invocation.`, "after_invoke"),
|
|
250
|
+
];
|
|
197
251
|
};
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
output.info("");
|
|
208
|
-
output.info("Config:");
|
|
209
|
-
output.info(` Base URL: ${payload.config.baseUrl} (${payload.config.baseUrlSource})`);
|
|
210
|
-
output.info(` Config dir: ${payload.config.configDir}`);
|
|
211
|
-
output.info(` Credentials path: ${payload.config.credentialsPath}`);
|
|
212
|
-
output.info("");
|
|
213
|
-
output.info("Auth:");
|
|
214
|
-
output.info(` API key source: ${payload.auth.apiKeySource}`);
|
|
215
|
-
output.info(` API key present: ${payload.auth.apiKeyPresent ? "yes" : "no"}`);
|
|
216
|
-
output.info(` API key fingerprint: ${payload.auth.apiKeyPreview ?? "none"}`);
|
|
217
|
-
output.info(` Credentials file exists: ${payload.auth.credentialsFileExists ? "yes" : "no"}`);
|
|
218
|
-
if (payload.auth.credentialsFileLoadError !== null) {
|
|
219
|
-
output.info(` Credentials load error: ${payload.auth.credentialsFileLoadError}`);
|
|
220
|
-
}
|
|
221
|
-
output.info("");
|
|
222
|
-
output.info("Remote:");
|
|
223
|
-
output.info(` Ping endpoint: ${payload.remote.ping.endpoint}`);
|
|
224
|
-
output.info(` Ping reachable: ${payload.remote.ping.reachable ? "yes" : "no"}`);
|
|
225
|
-
output.info(` Ping latency: ${formatLatency(payload.remote.ping.latencyMs)}`);
|
|
226
|
-
if (payload.remote.ping.status !== null) {
|
|
227
|
-
output.info(` Ping status: ${payload.remote.ping.status} ${payload.remote.ping.statusText ?? ""}`.trimEnd());
|
|
228
|
-
}
|
|
229
|
-
if (payload.remote.ping.error !== null) {
|
|
230
|
-
output.info(` Ping error: ${payload.remote.ping.error}`);
|
|
231
|
-
}
|
|
232
|
-
output.info(` Account check: ${payload.remote.account.status}`);
|
|
233
|
-
output.info(` Account latency: ${formatLatency(payload.remote.account.latencyMs)}`);
|
|
234
|
-
if (payload.remote.account.serverCount !== null) {
|
|
235
|
-
output.info(` Server count: ${payload.remote.account.serverCount}`);
|
|
236
|
-
}
|
|
237
|
-
if (payload.remote.account.orgCount !== null) {
|
|
238
|
-
output.info(` Organization count: ${payload.remote.account.orgCount}`);
|
|
239
|
-
}
|
|
240
|
-
if (payload.remote.account.serverPaths.length > 0) {
|
|
241
|
-
output.info(` Server paths: ${payload.remote.account.serverPaths.join(", ")}`);
|
|
242
|
-
}
|
|
243
|
-
if (payload.remote.account.message !== null) {
|
|
244
|
-
output.info(` Account message: ${payload.remote.account.message}`);
|
|
245
|
-
}
|
|
246
|
-
output.info("");
|
|
247
|
-
output.info("Documentation:");
|
|
248
|
-
output.info(` Config precedence: ${payload.documentation.configPrecedence.join(" -> ")}`);
|
|
249
|
-
output.info(" Quick commands:");
|
|
250
|
-
for (const command of payload.documentation.quickCommands) {
|
|
251
|
-
output.info(` ${command}`);
|
|
252
|
-
}
|
|
253
|
-
if (payload.summary.issues.length > 0) {
|
|
254
|
-
output.info("");
|
|
255
|
-
output.info("Issues:");
|
|
256
|
-
for (const issue of payload.summary.issues) {
|
|
257
|
-
output.info(` - ${issue}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
if (payload.summary.nextActions.length > 0) {
|
|
261
|
-
output.info("");
|
|
262
|
-
output.info("Next actions:");
|
|
263
|
-
for (const action of payload.summary.nextActions) {
|
|
264
|
-
output.info(` - ${action}`);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
252
|
+
const nextActionsForStatus = (payload) => {
|
|
253
|
+
if (!payload.auth.apiKeyPresent) {
|
|
254
|
+
return [
|
|
255
|
+
nextAction("login", "Authenticate", "ogment auth login", "Status detected no API key; authenticate first.", "immediate"),
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
return [
|
|
259
|
+
nextAction("discover_servers", "Discover servers", "ogment catalog", `Connectivity is ${payload.summary.status}; discover available servers.`, "after_status"),
|
|
260
|
+
];
|
|
267
261
|
};
|
|
268
262
|
const createProgram = (runtime) => {
|
|
269
263
|
const program = new Command();
|
|
@@ -275,8 +269,8 @@ const createProgram = (runtime) => {
|
|
|
275
269
|
.name(APP_NAME)
|
|
276
270
|
.description(APP_DESCRIPTION)
|
|
277
271
|
.version(VERSION)
|
|
278
|
-
.option("--
|
|
279
|
-
.option("--
|
|
272
|
+
.option("--api-key <key>", "API key override")
|
|
273
|
+
.option("--human", "Render output for humans")
|
|
280
274
|
.option("--quiet", "Suppress non-essential output")
|
|
281
275
|
.option("--non-interactive", "Disable interactive behavior")
|
|
282
276
|
.option("--yes", "Assume yes for any confirmation")
|
|
@@ -285,121 +279,381 @@ const createProgram = (runtime) => {
|
|
|
285
279
|
runtime.output.configure(mapGlobalOutputOptions(options));
|
|
286
280
|
runtime.context.apiKeyOverride = options.apiKey;
|
|
287
281
|
});
|
|
288
|
-
program
|
|
282
|
+
const authCommand = program.command("auth").description("Authentication workflows");
|
|
283
|
+
const runLoginFlow = async (mode, apiKey) => {
|
|
284
|
+
const loginInvocation = (() => {
|
|
285
|
+
if (mode === "api_key") {
|
|
286
|
+
return {
|
|
287
|
+
commandOptions: {
|
|
288
|
+
apiKey: apiKey ?? "",
|
|
289
|
+
mode: "apiKey",
|
|
290
|
+
},
|
|
291
|
+
invokedCommand: "ogment auth login --api-key <redacted>",
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (mode === "browser") {
|
|
295
|
+
return {
|
|
296
|
+
commandOptions: {
|
|
297
|
+
mode: "browser",
|
|
298
|
+
},
|
|
299
|
+
invokedCommand: "ogment auth login --browser",
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
const deviceCommand = "ogment auth login";
|
|
303
|
+
return {
|
|
304
|
+
commandOptions: {
|
|
305
|
+
mode: "device",
|
|
306
|
+
onPending: ({ userCode, verificationUri }) => {
|
|
307
|
+
const pendingNextActions = [
|
|
308
|
+
nextAction("check_login_status", "Check login status", "ogment auth status", `Approve ${userCode} at ${verificationUri}, then verify local auth state.`, "immediate"),
|
|
309
|
+
nextAction("restart_device_login", "Restart login", "ogment auth login", `Restart login if code ${userCode} expires before approval.`, "if_expired"),
|
|
310
|
+
];
|
|
311
|
+
runtime.output.success({
|
|
312
|
+
state: "pending",
|
|
313
|
+
userCode,
|
|
314
|
+
verificationUri,
|
|
315
|
+
}, {
|
|
316
|
+
command: deviceCommand,
|
|
317
|
+
entity: {
|
|
318
|
+
mode,
|
|
319
|
+
state: "pending",
|
|
320
|
+
userCode,
|
|
321
|
+
verificationUri,
|
|
322
|
+
},
|
|
323
|
+
humanMessage: `Open ${verificationUri} and enter code ${userCode}`,
|
|
324
|
+
nextActions: pendingNextActions,
|
|
325
|
+
});
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
invokedCommand: deviceCommand,
|
|
329
|
+
};
|
|
330
|
+
})();
|
|
331
|
+
const { commandOptions, invokedCommand } = loginInvocation;
|
|
332
|
+
const result = await runAuthLoginCommand(runtime.context, commandOptions);
|
|
333
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
334
|
+
command: invokedCommand,
|
|
335
|
+
entity: {
|
|
336
|
+
mode,
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
let humanMessage = `Logged in as ${data.agentName}.`;
|
|
340
|
+
if (data.alreadyLoggedIn) {
|
|
341
|
+
humanMessage = `Already logged in as ${data.agentName}.`;
|
|
342
|
+
}
|
|
343
|
+
else if (mode === "api_key") {
|
|
344
|
+
humanMessage = `Imported API key for ${data.agentName}.`;
|
|
345
|
+
}
|
|
346
|
+
runtime.output.success(data, {
|
|
347
|
+
command: invokedCommand,
|
|
348
|
+
entity: {
|
|
349
|
+
agentName: data.agentName,
|
|
350
|
+
alreadyLoggedIn: data.alreadyLoggedIn,
|
|
351
|
+
mode,
|
|
352
|
+
},
|
|
353
|
+
humanMessage,
|
|
354
|
+
nextActions: nextActionsForLogin(data, mode),
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
const loginWorkflowCommand = authCommand
|
|
289
358
|
.command("login")
|
|
290
|
-
.description("Authenticate with Ogment")
|
|
291
|
-
.option("--
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
359
|
+
.description("Authenticate with Ogment using device flow (recommended)")
|
|
360
|
+
.option("--browser", "Fallback: use browser callback flow");
|
|
361
|
+
loginWorkflowCommand.action(async (options, command) => {
|
|
362
|
+
const { apiKey } = asGlobalOptions(command);
|
|
363
|
+
if (options.browser === true && apiKey !== undefined) {
|
|
364
|
+
throw new ValidationError({
|
|
365
|
+
details: "--browser with --api-key",
|
|
366
|
+
message: "Use only one login mode: default device, --browser, or --api-key.",
|
|
367
|
+
suggestedCommand: "ogment auth login",
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
if (apiKey !== undefined) {
|
|
371
|
+
await runLoginFlow("api_key", apiKey);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (options.browser === true) {
|
|
375
|
+
await runLoginFlow("browser");
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
await runLoginFlow("device");
|
|
379
|
+
});
|
|
380
|
+
authCommand
|
|
381
|
+
.command("status")
|
|
382
|
+
.description("Show local authentication status")
|
|
383
|
+
.action(async () => {
|
|
384
|
+
const command = "ogment auth status";
|
|
385
|
+
const result = await runAuthStatusCommand(runtime.context);
|
|
386
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
387
|
+
command,
|
|
388
|
+
});
|
|
389
|
+
runtime.output.success(data, {
|
|
390
|
+
command,
|
|
391
|
+
entity: {
|
|
392
|
+
apiKeySource: data.apiKeySource,
|
|
393
|
+
loggedIn: data.loggedIn,
|
|
299
394
|
},
|
|
395
|
+
humanMessage: data.loggedIn
|
|
396
|
+
? `Authenticated (${data.apiKeySource}).`
|
|
397
|
+
: "Not authenticated.",
|
|
398
|
+
nextActions: nextActionsForAuthStatus(data),
|
|
300
399
|
});
|
|
301
|
-
const data = ensureSuccess(result, runtime.output);
|
|
302
|
-
const humanMessage = data.alreadyLoggedIn
|
|
303
|
-
? `Already logged in as ${data.agentName}.`
|
|
304
|
-
: `Logged in as ${data.agentName}. ${AGENT_SUCCESS_HINT}`;
|
|
305
|
-
runtime.output.success(data, humanMessage);
|
|
306
400
|
});
|
|
307
|
-
|
|
401
|
+
authCommand
|
|
308
402
|
.command("logout")
|
|
309
403
|
.description("Revoke token and delete local credentials")
|
|
310
404
|
.action(async () => {
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
|
|
405
|
+
const command = "ogment auth logout";
|
|
406
|
+
const result = await runAuthLogoutCommand(runtime.context);
|
|
407
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
408
|
+
command,
|
|
409
|
+
});
|
|
410
|
+
let message = "Not logged in.";
|
|
314
411
|
if (data.localCredentialsDeleted) {
|
|
315
|
-
|
|
412
|
+
message = data.revoked
|
|
316
413
|
? "Logged out and revoked API key."
|
|
317
|
-
: "
|
|
414
|
+
: "Logged out locally. Server revocation not confirmed.";
|
|
318
415
|
}
|
|
319
|
-
runtime.output.success(data,
|
|
416
|
+
runtime.output.success(data, {
|
|
417
|
+
command,
|
|
418
|
+
entity: {
|
|
419
|
+
localCredentialsDeleted: data.localCredentialsDeleted,
|
|
420
|
+
revoked: data.revoked,
|
|
421
|
+
},
|
|
422
|
+
humanMessage: message,
|
|
423
|
+
nextActions: [
|
|
424
|
+
nextAction("login", "Authenticate again", "ogment auth login", "Logout completed; authenticate again before further tool calls.", "after_logout"),
|
|
425
|
+
],
|
|
426
|
+
});
|
|
320
427
|
});
|
|
321
|
-
program
|
|
322
|
-
.command("
|
|
323
|
-
.description("
|
|
324
|
-
.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
428
|
+
const catalogCommand = program
|
|
429
|
+
.command("catalog")
|
|
430
|
+
.description("Discover servers and tools with progressive disclosure")
|
|
431
|
+
.option("--server <server-id>", "Filter to one server id")
|
|
432
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
433
|
+
.option("--limit <limit>", "Maximum items to return");
|
|
434
|
+
catalogCommand.action(async (options) => {
|
|
435
|
+
const parsedLimit = typeof options.limit === "string" ? Number.parseInt(options.limit, 10) : undefined;
|
|
436
|
+
const command = [
|
|
437
|
+
"ogment catalog",
|
|
438
|
+
...(options.server === undefined ? [] : [`--server ${options.server}`]),
|
|
439
|
+
...(options.cursor === undefined ? [] : [`--cursor ${options.cursor}`]),
|
|
440
|
+
...(parsedLimit === undefined || Number.isNaN(parsedLimit) ? [] : [`--limit ${parsedLimit}`]),
|
|
441
|
+
].join(" ");
|
|
442
|
+
if (options.server !== undefined && options.cursor !== undefined) {
|
|
443
|
+
throw new ValidationError({
|
|
444
|
+
details: `--server ${options.server} with --cursor ${options.cursor}`,
|
|
445
|
+
message: "Invalid catalog options. --server cannot be combined with --cursor.",
|
|
446
|
+
suggestedCommand: `ogment catalog --server ${options.server}`,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
if (options.limit !== undefined && (parsedLimit === undefined || Number.isNaN(parsedLimit))) {
|
|
450
|
+
throw new ValidationError({
|
|
451
|
+
details: options.limit,
|
|
452
|
+
message: "Invalid --limit value. Expected an integer.",
|
|
453
|
+
suggestedCommand: "ogment catalog --limit 20",
|
|
454
|
+
});
|
|
334
455
|
}
|
|
335
|
-
|
|
456
|
+
if (parsedLimit !== undefined && parsedLimit < 1) {
|
|
457
|
+
throw new ValidationError({
|
|
458
|
+
details: options.limit ?? String(parsedLimit),
|
|
459
|
+
message: "Invalid --limit value. Expected a positive integer.",
|
|
460
|
+
suggestedCommand: "ogment catalog --limit 20",
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
const result = await runCatalogCommand(runtime.context, {
|
|
464
|
+
cursor: options.cursor,
|
|
465
|
+
limit: parsedLimit,
|
|
466
|
+
serverId: options.server,
|
|
467
|
+
});
|
|
468
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
469
|
+
command,
|
|
470
|
+
entity: {
|
|
471
|
+
cursor: options.cursor ?? null,
|
|
472
|
+
limit: parsedLimit ?? null,
|
|
473
|
+
serverId: options.server ?? null,
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
const outputData = {
|
|
477
|
+
servers: data.servers,
|
|
478
|
+
};
|
|
479
|
+
runtime.output.success(outputData, {
|
|
480
|
+
command,
|
|
481
|
+
entity: {
|
|
482
|
+
cursor: options.cursor ?? null,
|
|
483
|
+
limit: parsedLimit ?? null,
|
|
484
|
+
serverCount: data.servers.length,
|
|
485
|
+
serverId: options.server ?? null,
|
|
486
|
+
},
|
|
487
|
+
humanMessage: data.nextCursor === null
|
|
488
|
+
? `${data.servers.length} server(s) available.`
|
|
489
|
+
: `${data.servers.length} server(s) shown. More available with --cursor ${data.nextCursor}.`,
|
|
490
|
+
nextActions: nextActionsForCatalogSummary(outputData, {
|
|
491
|
+
limit: parsedLimit ?? null,
|
|
492
|
+
nextCursor: data.nextCursor,
|
|
493
|
+
}, {
|
|
494
|
+
serverId: options.server ?? null,
|
|
495
|
+
}),
|
|
496
|
+
pagination: {
|
|
497
|
+
nextCursor: data.nextCursor,
|
|
498
|
+
},
|
|
499
|
+
});
|
|
336
500
|
});
|
|
337
|
-
|
|
338
|
-
.command("
|
|
339
|
-
.description("
|
|
340
|
-
.action(async (
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
501
|
+
catalogCommand
|
|
502
|
+
.command("tools <serverId>")
|
|
503
|
+
.description("List tool summaries for one server")
|
|
504
|
+
.action(async (serverId) => {
|
|
505
|
+
const command = `ogment catalog tools ${serverId}`;
|
|
506
|
+
const result = await runCatalogToolsCommand(runtime.context, { serverId });
|
|
507
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
508
|
+
command,
|
|
509
|
+
entity: {
|
|
510
|
+
serverId,
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
runtime.output.success(data, {
|
|
514
|
+
command,
|
|
515
|
+
entity: {
|
|
516
|
+
serverId: data.server.serverId,
|
|
517
|
+
toolCount: data.tools.length,
|
|
518
|
+
},
|
|
519
|
+
humanMessage: `${data.tools.length} tool(s) available on ${data.server.serverId}.`,
|
|
520
|
+
nextActions: nextActionsForCatalogTools(data),
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
catalogCommand
|
|
524
|
+
.command("tool <serverId> <toolName>")
|
|
525
|
+
.description("Inspect full schema for one tool")
|
|
526
|
+
.option("--example", "Include a generated example input payload")
|
|
527
|
+
.action(async (serverId, toolName, options) => {
|
|
528
|
+
const command = [
|
|
529
|
+
"ogment catalog tool",
|
|
530
|
+
serverId,
|
|
344
531
|
toolName,
|
|
532
|
+
...(options.example === true ? ["--example"] : []),
|
|
533
|
+
].join(" ");
|
|
534
|
+
const result = await runCatalogToolDetailsCommand(runtime.context, { serverId, toolName });
|
|
535
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
536
|
+
command,
|
|
537
|
+
entity: {
|
|
538
|
+
serverId,
|
|
539
|
+
toolName,
|
|
540
|
+
},
|
|
345
541
|
});
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
542
|
+
const exampleInput = buildJsonSchemaExample(data.inputSchema);
|
|
543
|
+
const outputData = options.example === true ? { ...data, exampleInput } : data;
|
|
544
|
+
runtime.output.success(outputData, {
|
|
545
|
+
command,
|
|
546
|
+
entity: {
|
|
547
|
+
serverId: data.server.serverId,
|
|
548
|
+
toolName: data.name,
|
|
549
|
+
},
|
|
550
|
+
humanMessage: `Loaded schema for ${data.server.serverId}/${data.name}.`,
|
|
551
|
+
nextActions: nextActionsForCatalogToolDetails(data, exampleInput),
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
program
|
|
555
|
+
.command("invoke <target>")
|
|
556
|
+
.description("Invoke a tool using <server-id>/<tool-name>")
|
|
557
|
+
.option("--input <value>", "Input payload: inline JSON object, @path, or - for stdin")
|
|
558
|
+
.action(async (target, options) => {
|
|
559
|
+
let inputSource = "none";
|
|
560
|
+
let command = `ogment invoke ${target}`;
|
|
561
|
+
if (options.input !== undefined) {
|
|
562
|
+
if (options.input === "-") {
|
|
563
|
+
inputSource = "stdin";
|
|
564
|
+
command = `ogment invoke ${target} --input -`;
|
|
565
|
+
}
|
|
566
|
+
else if (options.input.startsWith("@")) {
|
|
567
|
+
inputSource = "file";
|
|
568
|
+
command = `ogment invoke ${target} --input ${options.input}`;
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
inputSource = "inline_json";
|
|
572
|
+
command = `ogment invoke ${target} --input <json>`;
|
|
573
|
+
}
|
|
350
574
|
}
|
|
351
|
-
runtime.
|
|
575
|
+
const result = await runInvokeCommand(runtime.context, {
|
|
576
|
+
input: options.input,
|
|
577
|
+
target,
|
|
578
|
+
});
|
|
579
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
580
|
+
command,
|
|
581
|
+
entity: {
|
|
582
|
+
inputSource,
|
|
583
|
+
target,
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
runtime.output.success(data, {
|
|
587
|
+
command,
|
|
588
|
+
entity: {
|
|
589
|
+
inputSource,
|
|
590
|
+
serverId: data.serverId,
|
|
591
|
+
toolName: data.toolName,
|
|
592
|
+
},
|
|
593
|
+
humanMessage: `Invoked ${data.serverId}/${data.toolName}.`,
|
|
594
|
+
nextActions: nextActionsForInvoke(data),
|
|
595
|
+
});
|
|
352
596
|
});
|
|
353
597
|
program
|
|
354
|
-
.command("
|
|
598
|
+
.command("status")
|
|
355
599
|
.description("Show runtime configuration and connectivity diagnostics")
|
|
356
600
|
.action(async () => {
|
|
601
|
+
const command = "ogment status";
|
|
357
602
|
if (runtime.infoService === undefined) {
|
|
358
603
|
throw new UnexpectedError({
|
|
359
604
|
message: "Info service is not configured",
|
|
360
605
|
});
|
|
361
606
|
}
|
|
362
|
-
const result = await
|
|
607
|
+
const result = await runStatusCommand({
|
|
363
608
|
apiKeyOverride: runtime.context.apiKeyOverride,
|
|
364
609
|
}, {
|
|
365
610
|
infoService: runtime.infoService,
|
|
366
611
|
});
|
|
367
|
-
const data = ensureSuccess(result, runtime.output
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const result = runDescribeCommand();
|
|
379
|
-
const data = ensureSuccess(result, runtime.output);
|
|
380
|
-
runtime.output.json(data);
|
|
612
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
613
|
+
command,
|
|
614
|
+
});
|
|
615
|
+
runtime.output.success(data, {
|
|
616
|
+
command,
|
|
617
|
+
entity: {
|
|
618
|
+
summaryStatus: data.summary.status,
|
|
619
|
+
},
|
|
620
|
+
humanMessage: `Diagnostics status: ${data.summary.status.toUpperCase()}`,
|
|
621
|
+
nextActions: nextActionsForStatus(data),
|
|
622
|
+
});
|
|
381
623
|
});
|
|
382
624
|
program.action(() => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
625
|
+
runtime.output.success({
|
|
626
|
+
commands: [
|
|
627
|
+
"auth login",
|
|
628
|
+
"auth status",
|
|
629
|
+
"auth logout",
|
|
630
|
+
"catalog",
|
|
631
|
+
"catalog tools <server-id>",
|
|
632
|
+
"catalog tool <server-id> <tool-name>",
|
|
633
|
+
"invoke <server-id>/<tool-name>",
|
|
634
|
+
"status",
|
|
635
|
+
],
|
|
636
|
+
}, {
|
|
637
|
+
command: "ogment",
|
|
638
|
+
entity: null,
|
|
639
|
+
humanMessage: "Select a command to continue.",
|
|
640
|
+
nextActions: [
|
|
641
|
+
nextAction("login", "Authenticate", "ogment auth login", "Authenticate first so catalog and invoke commands can run.", "immediate"),
|
|
642
|
+
nextAction("catalog", "Discover servers", "ogment catalog", "List accessible servers and tool counts.", "after_auth"),
|
|
643
|
+
],
|
|
644
|
+
});
|
|
399
645
|
});
|
|
400
646
|
return program;
|
|
401
647
|
};
|
|
402
648
|
const normalizeCommanderError = (error) => {
|
|
649
|
+
if (error instanceof AuthError ||
|
|
650
|
+
error instanceof ContractMismatchError ||
|
|
651
|
+
error instanceof NotFoundError ||
|
|
652
|
+
error instanceof RemoteRequestError ||
|
|
653
|
+
error instanceof UnexpectedError ||
|
|
654
|
+
error instanceof ValidationError) {
|
|
655
|
+
return error;
|
|
656
|
+
}
|
|
403
657
|
if (error instanceof CommanderError) {
|
|
404
658
|
return new ValidationError({
|
|
405
659
|
details: error.message,
|
|
@@ -423,6 +677,13 @@ const normalizeCliArgv = (argv) => {
|
|
|
423
677
|
}
|
|
424
678
|
return argv;
|
|
425
679
|
};
|
|
680
|
+
const commandFromArgv = (argv) => {
|
|
681
|
+
const normalized = normalizeCliArgv(argv);
|
|
682
|
+
if (normalized.length === 0) {
|
|
683
|
+
return "ogment";
|
|
684
|
+
}
|
|
685
|
+
return `ogment ${normalized.join(" ")}`;
|
|
686
|
+
};
|
|
426
687
|
export const runCli = async (argv = process.argv.slice(2), runtime = createRuntime()) => {
|
|
427
688
|
const program = createProgram(runtime);
|
|
428
689
|
try {
|
|
@@ -437,7 +698,11 @@ export const runCli = async (argv = process.argv.slice(2), runtime = createRunti
|
|
|
437
698
|
return EXIT_CODE.success;
|
|
438
699
|
}
|
|
439
700
|
const normalized = normalizeCommanderError(error);
|
|
440
|
-
runtime.output.error(normalized
|
|
701
|
+
runtime.output.error(normalized, {
|
|
702
|
+
command: commandFromArgv(argv),
|
|
703
|
+
entity: null,
|
|
704
|
+
nextActions: nextActionsForError(normalized),
|
|
705
|
+
});
|
|
441
706
|
return exitCodeForError(normalized);
|
|
442
707
|
}
|
|
443
708
|
};
|