@ogment-ai/cli 0.3.5 → 0.4.1
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 +72 -76
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +521 -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 +101 -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,166 @@ 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
|
-
const propertiesValue = schema["properties"];
|
|
129
|
-
if (!isRecord(propertiesValue)) {
|
|
130
|
-
output.info(" Parameters: none");
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const entries = Object.entries(propertiesValue);
|
|
134
|
-
if (entries.length === 0) {
|
|
135
|
-
output.info(" Parameters: none");
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
output.info(" Parameters:");
|
|
139
|
-
for (const [name, propertyValue] of entries) {
|
|
140
|
-
if (!isRecord(propertyValue)) {
|
|
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}`);
|
|
150
|
-
}
|
|
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
|
+
];
|
|
151
211
|
};
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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"));
|
|
156
217
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
output.info(` ${line}`);
|
|
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"));
|
|
161
221
|
}
|
|
222
|
+
return actions;
|
|
162
223
|
};
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
: "";
|
|
172
|
-
output.info(`${server.path} (${server.orgSlug}) ${server.name}${description}`);
|
|
173
|
-
}
|
|
174
|
-
output.info("Inspect tools with: ogment servers <path>");
|
|
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
|
+
];
|
|
175
232
|
};
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return;
|
|
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
|
-
}
|
|
233
|
+
const toInlineJsonArgument = (value) => {
|
|
234
|
+
const serialized = JSON.stringify(value);
|
|
235
|
+
const escaped = serialized.replaceAll("'", String.raw `'\''`);
|
|
236
|
+
return `'${escaped}'`;
|
|
191
237
|
};
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
238
|
+
const nextActionsForCatalogToolDetails = (payload, exampleInput) => {
|
|
239
|
+
const inputArgument = toInlineJsonArgument(exampleInput);
|
|
240
|
+
return [
|
|
241
|
+
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"),
|
|
242
|
+
];
|
|
197
243
|
};
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
}
|
|
244
|
+
const nextActionsForInvoke = (payload) => {
|
|
245
|
+
return [
|
|
246
|
+
nextAction("inspect_tool", "Inspect tool schema", `ogment catalog ${payload.serverId} ${payload.toolName}`, `Review ${payload.serverId}/${payload.toolName} schema for the next invocation.`, "after_invoke"),
|
|
247
|
+
];
|
|
248
|
+
};
|
|
249
|
+
const nextActionsForStatus = (payload) => {
|
|
250
|
+
if (!payload.auth.apiKeyPresent) {
|
|
251
|
+
return [
|
|
252
|
+
nextAction("login", "Authenticate", "ogment auth login", "Status detected no API key; authenticate first.", "immediate"),
|
|
253
|
+
];
|
|
254
|
+
}
|
|
255
|
+
return [
|
|
256
|
+
nextAction("discover_servers", "Discover servers", "ogment catalog", `Connectivity is ${payload.summary.status}; discover available servers.`, "after_status"),
|
|
257
|
+
];
|
|
267
258
|
};
|
|
268
259
|
const createProgram = (runtime) => {
|
|
269
260
|
const program = new Command();
|
|
@@ -275,8 +266,8 @@ const createProgram = (runtime) => {
|
|
|
275
266
|
.name(APP_NAME)
|
|
276
267
|
.description(APP_DESCRIPTION)
|
|
277
268
|
.version(VERSION)
|
|
278
|
-
.option("--
|
|
279
|
-
.option("--
|
|
269
|
+
.option("--api-key <key>", "API key override")
|
|
270
|
+
.option("--human", "Render output for humans")
|
|
280
271
|
.option("--quiet", "Suppress non-essential output")
|
|
281
272
|
.option("--non-interactive", "Disable interactive behavior")
|
|
282
273
|
.option("--yes", "Assume yes for any confirmation")
|
|
@@ -285,121 +276,393 @@ const createProgram = (runtime) => {
|
|
|
285
276
|
runtime.output.configure(mapGlobalOutputOptions(options));
|
|
286
277
|
runtime.context.apiKeyOverride = options.apiKey;
|
|
287
278
|
});
|
|
288
|
-
program
|
|
279
|
+
const authCommand = program.command("auth").description("Authentication workflows");
|
|
280
|
+
const runLoginFlow = async (mode, apiKey) => {
|
|
281
|
+
const loginInvocation = (() => {
|
|
282
|
+
if (mode === "api_key") {
|
|
283
|
+
return {
|
|
284
|
+
commandOptions: {
|
|
285
|
+
apiKey: apiKey ?? "",
|
|
286
|
+
mode: "apiKey",
|
|
287
|
+
},
|
|
288
|
+
invokedCommand: "ogment auth login --api-key <redacted>",
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (mode === "browser") {
|
|
292
|
+
return {
|
|
293
|
+
commandOptions: {
|
|
294
|
+
mode: "browser",
|
|
295
|
+
},
|
|
296
|
+
invokedCommand: "ogment auth login --browser",
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const deviceCommand = "ogment auth login";
|
|
300
|
+
return {
|
|
301
|
+
commandOptions: {
|
|
302
|
+
mode: "device",
|
|
303
|
+
onPending: ({ userCode, verificationUri }) => {
|
|
304
|
+
const pendingNextActions = [
|
|
305
|
+
nextAction("check_login_status", "Check login status", "ogment auth status", `Approve ${userCode} at ${verificationUri}, then verify local auth state.`, "immediate"),
|
|
306
|
+
nextAction("restart_device_login", "Restart login", "ogment auth login", `Restart login if code ${userCode} expires before approval.`, "if_expired"),
|
|
307
|
+
];
|
|
308
|
+
runtime.output.success({
|
|
309
|
+
state: "pending",
|
|
310
|
+
userCode,
|
|
311
|
+
verificationUri,
|
|
312
|
+
}, {
|
|
313
|
+
command: deviceCommand,
|
|
314
|
+
entity: {
|
|
315
|
+
mode,
|
|
316
|
+
state: "pending",
|
|
317
|
+
userCode,
|
|
318
|
+
verificationUri,
|
|
319
|
+
},
|
|
320
|
+
humanMessage: `Open ${verificationUri} and enter code ${userCode}`,
|
|
321
|
+
nextActions: pendingNextActions,
|
|
322
|
+
});
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
invokedCommand: deviceCommand,
|
|
326
|
+
};
|
|
327
|
+
})();
|
|
328
|
+
const { commandOptions, invokedCommand } = loginInvocation;
|
|
329
|
+
const result = await runAuthLoginCommand(runtime.context, commandOptions);
|
|
330
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
331
|
+
command: invokedCommand,
|
|
332
|
+
entity: {
|
|
333
|
+
mode,
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
let humanMessage = `Logged in as ${data.agentName}.`;
|
|
337
|
+
if (data.alreadyLoggedIn) {
|
|
338
|
+
humanMessage = `Already logged in as ${data.agentName}.`;
|
|
339
|
+
}
|
|
340
|
+
else if (mode === "api_key") {
|
|
341
|
+
humanMessage = `Imported API key for ${data.agentName}.`;
|
|
342
|
+
}
|
|
343
|
+
runtime.output.success(data, {
|
|
344
|
+
command: invokedCommand,
|
|
345
|
+
entity: {
|
|
346
|
+
agentName: data.agentName,
|
|
347
|
+
alreadyLoggedIn: data.alreadyLoggedIn,
|
|
348
|
+
mode,
|
|
349
|
+
},
|
|
350
|
+
humanMessage,
|
|
351
|
+
nextActions: nextActionsForLogin(data, mode),
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
const loginWorkflowCommand = authCommand
|
|
289
355
|
.command("login")
|
|
290
|
-
.description("Authenticate with Ogment")
|
|
291
|
-
.option("--
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
356
|
+
.description("Authenticate with Ogment using device flow (recommended)")
|
|
357
|
+
.option("--browser", "Fallback: use browser callback flow");
|
|
358
|
+
loginWorkflowCommand.action(async (options, command) => {
|
|
359
|
+
const { apiKey } = asGlobalOptions(command);
|
|
360
|
+
if (options.browser === true && apiKey !== undefined) {
|
|
361
|
+
throw new ValidationError({
|
|
362
|
+
details: "--browser with --api-key",
|
|
363
|
+
message: "Use only one login mode: default device, --browser, or --api-key.",
|
|
364
|
+
suggestedCommand: "ogment auth login",
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
if (apiKey !== undefined) {
|
|
368
|
+
await runLoginFlow("api_key", apiKey);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (options.browser === true) {
|
|
372
|
+
await runLoginFlow("browser");
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
await runLoginFlow("device");
|
|
376
|
+
});
|
|
377
|
+
authCommand
|
|
378
|
+
.command("status")
|
|
379
|
+
.description("Show local authentication status")
|
|
380
|
+
.action(async () => {
|
|
381
|
+
const command = "ogment auth status";
|
|
382
|
+
const result = await runAuthStatusCommand(runtime.context);
|
|
383
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
384
|
+
command,
|
|
385
|
+
});
|
|
386
|
+
runtime.output.success(data, {
|
|
387
|
+
command,
|
|
388
|
+
entity: {
|
|
389
|
+
apiKeySource: data.apiKeySource,
|
|
390
|
+
loggedIn: data.loggedIn,
|
|
299
391
|
},
|
|
392
|
+
humanMessage: data.loggedIn
|
|
393
|
+
? `Authenticated (${data.apiKeySource}).`
|
|
394
|
+
: "Not authenticated.",
|
|
395
|
+
nextActions: nextActionsForAuthStatus(data),
|
|
300
396
|
});
|
|
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
397
|
});
|
|
307
|
-
|
|
398
|
+
authCommand
|
|
308
399
|
.command("logout")
|
|
309
400
|
.description("Revoke token and delete local credentials")
|
|
310
401
|
.action(async () => {
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
|
|
402
|
+
const command = "ogment auth logout";
|
|
403
|
+
const result = await runAuthLogoutCommand(runtime.context);
|
|
404
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
405
|
+
command,
|
|
406
|
+
});
|
|
407
|
+
let message = "Not logged in.";
|
|
314
408
|
if (data.localCredentialsDeleted) {
|
|
315
|
-
|
|
409
|
+
message = data.revoked
|
|
316
410
|
? "Logged out and revoked API key."
|
|
317
|
-
: "
|
|
411
|
+
: "Logged out locally. Server revocation not confirmed.";
|
|
318
412
|
}
|
|
319
|
-
runtime.output.success(data,
|
|
413
|
+
runtime.output.success(data, {
|
|
414
|
+
command,
|
|
415
|
+
entity: {
|
|
416
|
+
localCredentialsDeleted: data.localCredentialsDeleted,
|
|
417
|
+
revoked: data.revoked,
|
|
418
|
+
},
|
|
419
|
+
humanMessage: message,
|
|
420
|
+
nextActions: [
|
|
421
|
+
nextAction("login", "Authenticate again", "ogment auth login", "Logout completed; authenticate again before further tool calls.", "after_logout"),
|
|
422
|
+
],
|
|
423
|
+
});
|
|
320
424
|
});
|
|
321
|
-
program
|
|
322
|
-
.command("
|
|
323
|
-
.description("
|
|
324
|
-
.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
425
|
+
const catalogCommand = program
|
|
426
|
+
.command("catalog [serverId] [toolName]")
|
|
427
|
+
.description("Discover servers and tools with progressive disclosure")
|
|
428
|
+
.option("--cursor <cursor>", "Pagination cursor (catalog summary only)")
|
|
429
|
+
.option("--limit <limit>", "Maximum servers to return (catalog summary only)")
|
|
430
|
+
.option("--example", "Include a generated example input payload (tool details only)");
|
|
431
|
+
catalogCommand.action(async (serverId, toolName, options) => {
|
|
432
|
+
const parsedLimit = typeof options.limit === "string" ? Number.parseInt(options.limit, 10) : undefined;
|
|
433
|
+
if (options.limit !== undefined && (parsedLimit === undefined || Number.isNaN(parsedLimit))) {
|
|
434
|
+
throw new ValidationError({
|
|
435
|
+
details: options.limit,
|
|
436
|
+
message: "Invalid --limit value. Expected an integer.",
|
|
437
|
+
suggestedCommand: "ogment catalog --limit 20",
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
if (parsedLimit !== undefined && parsedLimit < 1) {
|
|
441
|
+
throw new ValidationError({
|
|
442
|
+
details: options.limit ?? String(parsedLimit),
|
|
443
|
+
message: "Invalid --limit value. Expected a positive integer.",
|
|
444
|
+
suggestedCommand: "ogment catalog --limit 20",
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
if (serverId === undefined) {
|
|
448
|
+
if (options.example === true) {
|
|
449
|
+
throw new ValidationError({
|
|
450
|
+
details: "--example without <server-id> <tool-name>",
|
|
451
|
+
message: "Invalid catalog options. --example requires <server-id> <tool-name>.",
|
|
452
|
+
suggestedCommand: "ogment catalog",
|
|
453
|
+
});
|
|
331
454
|
}
|
|
332
|
-
|
|
455
|
+
const command = [
|
|
456
|
+
"ogment catalog",
|
|
457
|
+
...(options.cursor === undefined ? [] : [`--cursor ${options.cursor}`]),
|
|
458
|
+
...(parsedLimit === undefined || Number.isNaN(parsedLimit)
|
|
459
|
+
? []
|
|
460
|
+
: [`--limit ${parsedLimit}`]),
|
|
461
|
+
].join(" ");
|
|
462
|
+
const result = await runCatalogCommand(runtime.context, {
|
|
463
|
+
cursor: options.cursor,
|
|
464
|
+
limit: parsedLimit,
|
|
465
|
+
});
|
|
466
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
467
|
+
command,
|
|
468
|
+
entity: {
|
|
469
|
+
cursor: options.cursor ?? null,
|
|
470
|
+
limit: parsedLimit ?? null,
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
const outputData = {
|
|
474
|
+
servers: data.servers,
|
|
475
|
+
};
|
|
476
|
+
runtime.output.success(outputData, {
|
|
477
|
+
command,
|
|
478
|
+
entity: {
|
|
479
|
+
cursor: options.cursor ?? null,
|
|
480
|
+
limit: parsedLimit ?? null,
|
|
481
|
+
serverCount: data.servers.length,
|
|
482
|
+
},
|
|
483
|
+
humanMessage: data.nextCursor === null
|
|
484
|
+
? `${data.servers.length} server(s) available.`
|
|
485
|
+
: `${data.servers.length} server(s) shown. More available with --cursor ${data.nextCursor}.`,
|
|
486
|
+
nextActions: nextActionsForCatalogSummary(outputData, {
|
|
487
|
+
limit: parsedLimit ?? null,
|
|
488
|
+
nextCursor: data.nextCursor,
|
|
489
|
+
}),
|
|
490
|
+
pagination: {
|
|
491
|
+
nextCursor: data.nextCursor,
|
|
492
|
+
},
|
|
493
|
+
});
|
|
333
494
|
return;
|
|
334
495
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
496
|
+
if (toolName === undefined) {
|
|
497
|
+
if (options.cursor !== undefined || parsedLimit !== undefined) {
|
|
498
|
+
throw new ValidationError({
|
|
499
|
+
details: `${options.cursor === undefined ? "" : `--cursor ${options.cursor} `}${parsedLimit === undefined ? "" : `--limit ${parsedLimit}`}`.trim(),
|
|
500
|
+
message: "Invalid catalog options. --cursor and --limit are only valid for `ogment catalog`.",
|
|
501
|
+
suggestedCommand: "ogment catalog",
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
if (options.example === true) {
|
|
505
|
+
throw new ValidationError({
|
|
506
|
+
details: `--example without tool name for ${serverId}`,
|
|
507
|
+
message: "Invalid catalog options. --example requires <tool-name>.",
|
|
508
|
+
suggestedCommand: `ogment catalog ${serverId}`,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
const command = `ogment catalog ${serverId}`;
|
|
512
|
+
const result = await runCatalogToolsCommand(runtime.context, { serverId });
|
|
513
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
514
|
+
command,
|
|
515
|
+
entity: {
|
|
516
|
+
serverId,
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
runtime.output.success(data, {
|
|
520
|
+
command,
|
|
521
|
+
entity: {
|
|
522
|
+
serverId: data.server.serverId,
|
|
523
|
+
toolCount: data.tools.length,
|
|
524
|
+
},
|
|
525
|
+
humanMessage: `${data.tools.length} tool(s) available on ${data.server.serverId}.`,
|
|
526
|
+
nextActions: nextActionsForCatalogTools(data),
|
|
527
|
+
});
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
if (options.cursor !== undefined || parsedLimit !== undefined) {
|
|
531
|
+
throw new ValidationError({
|
|
532
|
+
details: `${options.cursor === undefined ? "" : `--cursor ${options.cursor} `}${parsedLimit === undefined ? "" : `--limit ${parsedLimit}`}`.trim(),
|
|
533
|
+
message: "Invalid catalog options. --cursor and --limit are only valid for `ogment catalog`.",
|
|
534
|
+
suggestedCommand: `ogment catalog ${serverId} ${toolName}${options.example === true ? " --example" : ""}`,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
const command = [
|
|
538
|
+
"ogment catalog",
|
|
539
|
+
serverId,
|
|
344
540
|
toolName,
|
|
541
|
+
...(options.example === true ? ["--example"] : []),
|
|
542
|
+
].join(" ");
|
|
543
|
+
const result = await runCatalogToolDetailsCommand(runtime.context, { serverId, toolName });
|
|
544
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
545
|
+
command,
|
|
546
|
+
entity: {
|
|
547
|
+
serverId,
|
|
548
|
+
toolName,
|
|
549
|
+
},
|
|
345
550
|
});
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
551
|
+
const exampleInput = buildJsonSchemaExample(data.inputSchema);
|
|
552
|
+
const outputData = options.example === true ? { ...data, exampleInput } : data;
|
|
553
|
+
runtime.output.success(outputData, {
|
|
554
|
+
command,
|
|
555
|
+
entity: {
|
|
556
|
+
serverId: data.server.serverId,
|
|
557
|
+
toolName: data.name,
|
|
558
|
+
},
|
|
559
|
+
humanMessage: `Loaded schema for ${data.server.serverId}/${data.name}.`,
|
|
560
|
+
nextActions: nextActionsForCatalogToolDetails(data, exampleInput),
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
program
|
|
564
|
+
.command("invoke <target>")
|
|
565
|
+
.description("Invoke a tool using <server-id>/<tool-name>")
|
|
566
|
+
.option("--input <value>", "Input payload: inline JSON object, @path, or - for stdin")
|
|
567
|
+
.action(async (target, options) => {
|
|
568
|
+
let inputSource = "none";
|
|
569
|
+
let command = `ogment invoke ${target}`;
|
|
570
|
+
if (options.input !== undefined) {
|
|
571
|
+
if (options.input === "-") {
|
|
572
|
+
inputSource = "stdin";
|
|
573
|
+
command = `ogment invoke ${target} --input -`;
|
|
574
|
+
}
|
|
575
|
+
else if (options.input.startsWith("@")) {
|
|
576
|
+
inputSource = "file";
|
|
577
|
+
command = `ogment invoke ${target} --input ${options.input}`;
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
inputSource = "inline_json";
|
|
581
|
+
command = `ogment invoke ${target} --input <json>`;
|
|
582
|
+
}
|
|
350
583
|
}
|
|
351
|
-
runtime.
|
|
584
|
+
const result = await runInvokeCommand(runtime.context, {
|
|
585
|
+
input: options.input,
|
|
586
|
+
target,
|
|
587
|
+
});
|
|
588
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
589
|
+
command,
|
|
590
|
+
entity: {
|
|
591
|
+
inputSource,
|
|
592
|
+
target,
|
|
593
|
+
},
|
|
594
|
+
});
|
|
595
|
+
runtime.output.success(data, {
|
|
596
|
+
command,
|
|
597
|
+
entity: {
|
|
598
|
+
inputSource,
|
|
599
|
+
serverId: data.serverId,
|
|
600
|
+
toolName: data.toolName,
|
|
601
|
+
},
|
|
602
|
+
humanMessage: `Invoked ${data.serverId}/${data.toolName}.`,
|
|
603
|
+
nextActions: nextActionsForInvoke(data),
|
|
604
|
+
});
|
|
352
605
|
});
|
|
353
606
|
program
|
|
354
|
-
.command("
|
|
607
|
+
.command("status")
|
|
355
608
|
.description("Show runtime configuration and connectivity diagnostics")
|
|
356
609
|
.action(async () => {
|
|
610
|
+
const command = "ogment status";
|
|
357
611
|
if (runtime.infoService === undefined) {
|
|
358
612
|
throw new UnexpectedError({
|
|
359
613
|
message: "Info service is not configured",
|
|
360
614
|
});
|
|
361
615
|
}
|
|
362
|
-
const result = await
|
|
616
|
+
const result = await runStatusCommand({
|
|
363
617
|
apiKeyOverride: runtime.context.apiKeyOverride,
|
|
364
618
|
}, {
|
|
365
619
|
infoService: runtime.infoService,
|
|
366
620
|
});
|
|
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);
|
|
621
|
+
const data = ensureSuccess(result, runtime.output, {
|
|
622
|
+
command,
|
|
623
|
+
});
|
|
624
|
+
runtime.output.success(data, {
|
|
625
|
+
command,
|
|
626
|
+
entity: {
|
|
627
|
+
summaryStatus: data.summary.status,
|
|
628
|
+
},
|
|
629
|
+
humanMessage: `Diagnostics status: ${data.summary.status.toUpperCase()}`,
|
|
630
|
+
nextActions: nextActionsForStatus(data),
|
|
631
|
+
});
|
|
381
632
|
});
|
|
382
633
|
program.action(() => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
634
|
+
runtime.output.success({
|
|
635
|
+
commands: [
|
|
636
|
+
"auth login",
|
|
637
|
+
"auth status",
|
|
638
|
+
"auth logout",
|
|
639
|
+
"catalog",
|
|
640
|
+
"catalog <server-id>",
|
|
641
|
+
"catalog <server-id> <tool-name>",
|
|
642
|
+
"invoke <server-id>/<tool-name>",
|
|
643
|
+
"status",
|
|
644
|
+
],
|
|
645
|
+
}, {
|
|
646
|
+
command: "ogment",
|
|
647
|
+
entity: null,
|
|
648
|
+
humanMessage: "Select a command to continue.",
|
|
649
|
+
nextActions: [
|
|
650
|
+
nextAction("login", "Authenticate", "ogment auth login", "Authenticate first so catalog and invoke commands can run.", "immediate"),
|
|
651
|
+
nextAction("catalog", "Discover servers", "ogment catalog", "List accessible servers and tool counts.", "after_auth"),
|
|
652
|
+
],
|
|
653
|
+
});
|
|
399
654
|
});
|
|
400
655
|
return program;
|
|
401
656
|
};
|
|
402
657
|
const normalizeCommanderError = (error) => {
|
|
658
|
+
if (error instanceof AuthError ||
|
|
659
|
+
error instanceof ContractMismatchError ||
|
|
660
|
+
error instanceof NotFoundError ||
|
|
661
|
+
error instanceof RemoteRequestError ||
|
|
662
|
+
error instanceof UnexpectedError ||
|
|
663
|
+
error instanceof ValidationError) {
|
|
664
|
+
return error;
|
|
665
|
+
}
|
|
403
666
|
if (error instanceof CommanderError) {
|
|
404
667
|
return new ValidationError({
|
|
405
668
|
details: error.message,
|
|
@@ -423,6 +686,13 @@ const normalizeCliArgv = (argv) => {
|
|
|
423
686
|
}
|
|
424
687
|
return argv;
|
|
425
688
|
};
|
|
689
|
+
const commandFromArgv = (argv) => {
|
|
690
|
+
const normalized = normalizeCliArgv(argv);
|
|
691
|
+
if (normalized.length === 0) {
|
|
692
|
+
return "ogment";
|
|
693
|
+
}
|
|
694
|
+
return `ogment ${normalized.join(" ")}`;
|
|
695
|
+
};
|
|
426
696
|
export const runCli = async (argv = process.argv.slice(2), runtime = createRuntime()) => {
|
|
427
697
|
const program = createProgram(runtime);
|
|
428
698
|
try {
|
|
@@ -437,7 +707,11 @@ export const runCli = async (argv = process.argv.slice(2), runtime = createRunti
|
|
|
437
707
|
return EXIT_CODE.success;
|
|
438
708
|
}
|
|
439
709
|
const normalized = normalizeCommanderError(error);
|
|
440
|
-
runtime.output.error(normalized
|
|
710
|
+
runtime.output.error(normalized, {
|
|
711
|
+
command: commandFromArgv(argv),
|
|
712
|
+
entity: null,
|
|
713
|
+
nextActions: nextActionsForError(normalized),
|
|
714
|
+
});
|
|
441
715
|
return exitCodeForError(normalized);
|
|
442
716
|
}
|
|
443
717
|
};
|