@ogment-ai/cli 0.6.0 → 0.7.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/dist/cli.js +21382 -4
- package/dist/cli.js.map +1 -0
- package/package.json +44 -53
- package/README.md +0 -134
- package/dist/cli/commands.d.ts +0 -37
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -56
- package/dist/cli/execute.d.ts +0 -11
- package/dist/cli/execute.d.ts.map +0 -1
- package/dist/cli/execute.js +0 -468
- package/dist/cli/invocations.d.ts +0 -31
- package/dist/cli/invocations.d.ts.map +0 -1
- package/dist/cli/invocations.js +0 -1
- package/dist/cli/parse-errors.d.ts +0 -17
- package/dist/cli/parse-errors.d.ts.map +0 -1
- package/dist/cli/parse-errors.js +0 -184
- package/dist/cli/program.d.ts +0 -10
- package/dist/cli/program.d.ts.map +0 -1
- package/dist/cli/program.js +0 -174
- package/dist/cli/run.d.ts +0 -6
- package/dist/cli/run.d.ts.map +0 -1
- package/dist/cli/run.js +0 -83
- package/dist/cli/runtime.d.ts +0 -21
- package/dist/cli/runtime.d.ts.map +0 -1
- package/dist/cli/runtime.js +0 -80
- package/dist/cli.d.ts +0 -4
- package/dist/cli.d.ts.map +0 -1
- package/dist/commands/auth.d.ts +0 -19
- package/dist/commands/auth.d.ts.map +0 -1
- package/dist/commands/auth.js +0 -21
- package/dist/commands/catalog.d.ts +0 -31
- package/dist/commands/catalog.d.ts.map +0 -1
- package/dist/commands/catalog.js +0 -167
- package/dist/commands/context.d.ts +0 -15
- package/dist/commands/context.d.ts.map +0 -1
- package/dist/commands/context.js +0 -1
- package/dist/commands/invoke.d.ts +0 -17
- package/dist/commands/invoke.d.ts.map +0 -1
- package/dist/commands/invoke.js +0 -173
- package/dist/commands/server-context.d.ts +0 -14
- package/dist/commands/server-context.d.ts.map +0 -1
- package/dist/commands/server-context.js +0 -26
- package/dist/commands/status.d.ts +0 -12
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -5
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3
- package/dist/infra/credentials.d.ts +0 -22
- package/dist/infra/credentials.d.ts.map +0 -1
- package/dist/infra/credentials.js +0 -74
- package/dist/infra/env.d.ts +0 -15
- package/dist/infra/env.d.ts.map +0 -1
- package/dist/infra/env.js +0 -100
- package/dist/infra/http.d.ts +0 -16
- package/dist/infra/http.d.ts.map +0 -1
- package/dist/infra/http.js +0 -84
- package/dist/output/envelope.d.ts +0 -22
- package/dist/output/envelope.d.ts.map +0 -1
- package/dist/output/envelope.js +0 -67
- package/dist/output/manager.d.ts +0 -47
- package/dist/output/manager.d.ts.map +0 -1
- package/dist/output/manager.js +0 -120
- package/dist/services/account.d.ts +0 -16
- package/dist/services/account.d.ts.map +0 -1
- package/dist/services/account.js +0 -75
- package/dist/services/auth.d.ts +0 -37
- package/dist/services/auth.d.ts.map +0 -1
- package/dist/services/auth.js +0 -270
- package/dist/services/info.d.ts +0 -24
- package/dist/services/info.d.ts.map +0 -1
- package/dist/services/info.js +0 -316
- package/dist/services/mcp-error-mapping.d.ts +0 -9
- package/dist/services/mcp-error-mapping.d.ts.map +0 -1
- package/dist/services/mcp-error-mapping.js +0 -129
- package/dist/services/mcp.d.ts +0 -39
- package/dist/services/mcp.d.ts.map +0 -1
- package/dist/services/mcp.js +0 -169
- package/dist/shared/constants.d.ts +0 -5
- package/dist/shared/constants.d.ts.map +0 -1
- package/dist/shared/constants.js +0 -6
- package/dist/shared/error-codes.d.ts +0 -31
- package/dist/shared/error-codes.d.ts.map +0 -1
- package/dist/shared/error-codes.js +0 -25
- package/dist/shared/error-presentation.d.ts +0 -17
- package/dist/shared/error-presentation.d.ts.map +0 -1
- package/dist/shared/error-presentation.js +0 -151
- package/dist/shared/errors.d.ts +0 -146
- package/dist/shared/errors.d.ts.map +0 -1
- package/dist/shared/errors.js +0 -233
- package/dist/shared/exit-codes.d.ts +0 -15
- package/dist/shared/exit-codes.d.ts.map +0 -1
- package/dist/shared/exit-codes.js +0 -44
- package/dist/shared/guards.d.ts +0 -11
- package/dist/shared/guards.d.ts.map +0 -1
- package/dist/shared/guards.js +0 -29
- package/dist/shared/recovery.d.ts +0 -5
- package/dist/shared/recovery.d.ts.map +0 -1
- package/dist/shared/recovery.js +0 -123
- package/dist/shared/schema-example.d.ts +0 -3
- package/dist/shared/schema-example.d.ts.map +0 -1
- package/dist/shared/schema-example.js +0 -105
- package/dist/shared/schemas.d.ts +0 -14
- package/dist/shared/schemas.d.ts.map +0 -1
- package/dist/shared/schemas.js +0 -14
- package/dist/shared/types.d.ts +0 -225
- package/dist/shared/types.d.ts.map +0 -1
- package/dist/shared/types.js +0 -1
package/dist/services/auth.js
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { Result } from "better-result";
|
|
2
|
-
import { readResponseText } from "../infra/http.js";
|
|
3
|
-
import { ERROR_CODE } from "../shared/error-codes.js";
|
|
4
|
-
import { AuthError, RemoteRequestError, UnexpectedError, ValidationError, } from "../shared/errors.js";
|
|
5
|
-
import { parseWithSchema } from "../shared/guards.js";
|
|
6
|
-
import { deviceCodeStartSchema, deviceTokenApprovedSchema } from "../shared/schemas.js";
|
|
7
|
-
const defaultSleep = async (milliseconds) => {
|
|
8
|
-
await new Promise((resolve) => {
|
|
9
|
-
setTimeout(resolve, milliseconds);
|
|
10
|
-
});
|
|
11
|
-
};
|
|
12
|
-
const toPendingPayload = (payload) => {
|
|
13
|
-
if (typeof payload !== "object" || payload === null) {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
if (!Object.hasOwn(payload, "error")) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
const error = payload.error;
|
|
20
|
-
if (error === "authorization_pending") {
|
|
21
|
-
return "authorization_pending";
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
};
|
|
25
|
-
const deviceCodeUrl = (baseUrl) => `${baseUrl}/api/v1/mcp-auth/device/code`;
|
|
26
|
-
const deviceTokenUrl = (baseUrl) => `${baseUrl}/api/v1/mcp-auth/device/token`;
|
|
27
|
-
const revokeUrl = (baseUrl) => `${baseUrl}/api/v1/mcp-auth/cli/revoke-self`;
|
|
28
|
-
export const createAuthService = (deps) => {
|
|
29
|
-
const now = deps.now ?? Date.now;
|
|
30
|
-
const sleep = deps.sleep ?? defaultSleep;
|
|
31
|
-
const requestRemote = async (input, init) => {
|
|
32
|
-
return deps.httpClient.request(input, init);
|
|
33
|
-
};
|
|
34
|
-
const loginWithDevice = async (options) => {
|
|
35
|
-
const startFlowResponse = await requestRemote(deviceCodeUrl(deps.baseUrl), {
|
|
36
|
-
headers: {
|
|
37
|
-
"Content-Type": "application/json",
|
|
38
|
-
},
|
|
39
|
-
method: "POST",
|
|
40
|
-
});
|
|
41
|
-
if (Result.isError(startFlowResponse)) {
|
|
42
|
-
return startFlowResponse;
|
|
43
|
-
}
|
|
44
|
-
if (!startFlowResponse.value.ok) {
|
|
45
|
-
const body = await readResponseText(startFlowResponse.value);
|
|
46
|
-
return Result.err(new RemoteRequestError({
|
|
47
|
-
body,
|
|
48
|
-
httpStatus: startFlowResponse.value.status,
|
|
49
|
-
message: "Failed to start device login",
|
|
50
|
-
operation: "auth/device/start",
|
|
51
|
-
raw: body,
|
|
52
|
-
source: "http",
|
|
53
|
-
}));
|
|
54
|
-
}
|
|
55
|
-
const startPayload = await Result.tryPromise({
|
|
56
|
-
catch: () => new RemoteRequestError({
|
|
57
|
-
httpStatus: startFlowResponse.value.status,
|
|
58
|
-
message: "Failed to parse device login start payload",
|
|
59
|
-
operation: "auth/device/start",
|
|
60
|
-
source: "http",
|
|
61
|
-
}),
|
|
62
|
-
try: async () => startFlowResponse.value.json(),
|
|
63
|
-
});
|
|
64
|
-
if (Result.isError(startPayload)) {
|
|
65
|
-
return startPayload;
|
|
66
|
-
}
|
|
67
|
-
const parsedStartPayload = parseWithSchema(deviceCodeStartSchema, startPayload.value, "device login start response");
|
|
68
|
-
if (Result.isError(parsedStartPayload)) {
|
|
69
|
-
return parsedStartPayload;
|
|
70
|
-
}
|
|
71
|
-
options.onPending?.({
|
|
72
|
-
userCode: parsedStartPayload.value.data.user_code,
|
|
73
|
-
verificationUri: parsedStartPayload.value.data.verification_uri,
|
|
74
|
-
});
|
|
75
|
-
const deadline = now() + parsedStartPayload.value.data.expires_in * 1000;
|
|
76
|
-
const interval = parsedStartPayload.value.data.interval * 1000;
|
|
77
|
-
while (now() < deadline) {
|
|
78
|
-
await sleep(interval);
|
|
79
|
-
const pollResponse = await requestRemote(deviceTokenUrl(deps.baseUrl), {
|
|
80
|
-
body: JSON.stringify({
|
|
81
|
-
device_code: parsedStartPayload.value.data.device_code,
|
|
82
|
-
}),
|
|
83
|
-
headers: {
|
|
84
|
-
"Content-Type": "application/json",
|
|
85
|
-
},
|
|
86
|
-
method: "POST",
|
|
87
|
-
});
|
|
88
|
-
if (Result.isError(pollResponse)) {
|
|
89
|
-
return pollResponse;
|
|
90
|
-
}
|
|
91
|
-
if (pollResponse.value.status === 410) {
|
|
92
|
-
return Result.err(new AuthError({
|
|
93
|
-
code: ERROR_CODE.authDeviceExpired,
|
|
94
|
-
message: "Device login code expired. Run `ogment auth login` again.",
|
|
95
|
-
recovery: { command: "ogment auth login" },
|
|
96
|
-
}));
|
|
97
|
-
}
|
|
98
|
-
if (pollResponse.value.status === 404) {
|
|
99
|
-
return Result.err(new AuthError({
|
|
100
|
-
code: ERROR_CODE.authInvalidCredentials,
|
|
101
|
-
message: "Invalid device login code. Run `ogment auth login` again.",
|
|
102
|
-
recovery: { command: "ogment auth login" },
|
|
103
|
-
}));
|
|
104
|
-
}
|
|
105
|
-
if (!pollResponse.value.ok) {
|
|
106
|
-
const body = await readResponseText(pollResponse.value);
|
|
107
|
-
return Result.err(new RemoteRequestError({
|
|
108
|
-
body,
|
|
109
|
-
httpStatus: pollResponse.value.status,
|
|
110
|
-
message: "Failed to poll device login status",
|
|
111
|
-
operation: "auth/device/poll",
|
|
112
|
-
raw: body,
|
|
113
|
-
source: "http",
|
|
114
|
-
}));
|
|
115
|
-
}
|
|
116
|
-
const pollPayload = await Result.tryPromise({
|
|
117
|
-
catch: () => new RemoteRequestError({
|
|
118
|
-
httpStatus: pollResponse.value.status,
|
|
119
|
-
message: "Failed to parse device login poll payload",
|
|
120
|
-
operation: "auth/device/poll",
|
|
121
|
-
source: "http",
|
|
122
|
-
}),
|
|
123
|
-
try: async () => pollResponse.value.json(),
|
|
124
|
-
});
|
|
125
|
-
if (Result.isError(pollPayload)) {
|
|
126
|
-
return pollPayload;
|
|
127
|
-
}
|
|
128
|
-
if (toPendingPayload(pollPayload.value) === "authorization_pending") {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
const approvedPayload = parseWithSchema(deviceTokenApprovedSchema, pollPayload.value, "device login poll response");
|
|
132
|
-
if (Result.isError(approvedPayload)) {
|
|
133
|
-
return approvedPayload;
|
|
134
|
-
}
|
|
135
|
-
const saveResult = deps.credentialsStore.save({
|
|
136
|
-
agentName: approvedPayload.value.data.agent_name ?? "CLI Agent",
|
|
137
|
-
apiKey: approvedPayload.value.data.api_key,
|
|
138
|
-
});
|
|
139
|
-
if (Result.isError(saveResult)) {
|
|
140
|
-
return saveResult;
|
|
141
|
-
}
|
|
142
|
-
return Result.ok({
|
|
143
|
-
agentName: approvedPayload.value.data.agent_name ?? "CLI Agent",
|
|
144
|
-
loggedIn: true,
|
|
145
|
-
outcome: "authenticated",
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return Result.err(new AuthError({
|
|
149
|
-
code: ERROR_CODE.authDeviceExpired,
|
|
150
|
-
message: "Device login code expired. Run `ogment auth login` again.",
|
|
151
|
-
recovery: { command: "ogment auth login" },
|
|
152
|
-
}));
|
|
153
|
-
};
|
|
154
|
-
return {
|
|
155
|
-
login: async (options) => {
|
|
156
|
-
if (options.mode === "apiKey" && options.apiKey.length > 0) {
|
|
157
|
-
const saveResult = deps.credentialsStore.save({
|
|
158
|
-
agentName: "CLI Agent",
|
|
159
|
-
apiKey: options.apiKey,
|
|
160
|
-
});
|
|
161
|
-
if (Result.isError(saveResult)) {
|
|
162
|
-
return saveResult;
|
|
163
|
-
}
|
|
164
|
-
return Result.ok({
|
|
165
|
-
agentName: "CLI Agent",
|
|
166
|
-
loggedIn: true,
|
|
167
|
-
outcome: "authenticated",
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
if (options.mode === "apiKey") {
|
|
171
|
-
return Result.err(new ValidationError({
|
|
172
|
-
code: ERROR_CODE.validationInvalidInput,
|
|
173
|
-
message: "Missing API key value. Provide a non-empty API key.",
|
|
174
|
-
recovery: { command: "ogment auth login --api-key <key>" },
|
|
175
|
-
}));
|
|
176
|
-
}
|
|
177
|
-
const stored = deps.credentialsStore.load();
|
|
178
|
-
if (Result.isError(stored)) {
|
|
179
|
-
return stored;
|
|
180
|
-
}
|
|
181
|
-
if (stored.value !== null) {
|
|
182
|
-
return Result.ok({
|
|
183
|
-
agentName: stored.value.agentName ?? "CLI Agent",
|
|
184
|
-
loggedIn: true,
|
|
185
|
-
outcome: "already_authenticated",
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
return loginWithDevice(options);
|
|
189
|
-
},
|
|
190
|
-
logout: async () => {
|
|
191
|
-
const stored = deps.credentialsStore.load();
|
|
192
|
-
if (Result.isError(stored)) {
|
|
193
|
-
return stored;
|
|
194
|
-
}
|
|
195
|
-
if (stored.value === null) {
|
|
196
|
-
return Result.ok({
|
|
197
|
-
localCredentialsDeleted: false,
|
|
198
|
-
revoked: false,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
const revokeResult = await requestRemote(revokeUrl(deps.baseUrl), {
|
|
202
|
-
headers: {
|
|
203
|
-
Authorization: `Bearer ${stored.value.apiKey}`,
|
|
204
|
-
},
|
|
205
|
-
method: "POST",
|
|
206
|
-
});
|
|
207
|
-
const deleteResult = deps.credentialsStore.delete();
|
|
208
|
-
if (Result.isError(deleteResult)) {
|
|
209
|
-
return deleteResult;
|
|
210
|
-
}
|
|
211
|
-
const revoked = Result.isOk(revokeResult) && revokeResult.value.ok;
|
|
212
|
-
return Result.ok({
|
|
213
|
-
localCredentialsDeleted: true,
|
|
214
|
-
revoked,
|
|
215
|
-
});
|
|
216
|
-
},
|
|
217
|
-
resolveApiKey: async (overrideApiKey) => {
|
|
218
|
-
if (typeof overrideApiKey === "string" && overrideApiKey.length > 0) {
|
|
219
|
-
return Result.ok(overrideApiKey);
|
|
220
|
-
}
|
|
221
|
-
if (typeof deps.envApiKey === "string" && deps.envApiKey.length > 0) {
|
|
222
|
-
return Result.ok(deps.envApiKey);
|
|
223
|
-
}
|
|
224
|
-
const stored = deps.credentialsStore.load();
|
|
225
|
-
if (Result.isError(stored)) {
|
|
226
|
-
return stored;
|
|
227
|
-
}
|
|
228
|
-
if (stored.value !== null) {
|
|
229
|
-
return Result.ok(stored.value.apiKey);
|
|
230
|
-
}
|
|
231
|
-
return Result.err(new AuthError({
|
|
232
|
-
code: ERROR_CODE.authRequired,
|
|
233
|
-
message: "Not logged in. Run `ogment auth login` or set OGMENT_API_KEY.",
|
|
234
|
-
recovery: { command: "ogment auth login" },
|
|
235
|
-
}));
|
|
236
|
-
},
|
|
237
|
-
status: async (overrideApiKey) => {
|
|
238
|
-
if (typeof overrideApiKey === "string" && overrideApiKey.length > 0) {
|
|
239
|
-
return Result.ok({
|
|
240
|
-
agentName: null,
|
|
241
|
-
apiKeySource: "apiKeyOption",
|
|
242
|
-
loggedIn: true,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
if (typeof deps.envApiKey === "string" && deps.envApiKey.length > 0) {
|
|
246
|
-
return Result.ok({
|
|
247
|
-
agentName: null,
|
|
248
|
-
apiKeySource: "env",
|
|
249
|
-
loggedIn: true,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
const stored = deps.credentialsStore.load();
|
|
253
|
-
if (Result.isError(stored)) {
|
|
254
|
-
return stored;
|
|
255
|
-
}
|
|
256
|
-
if (stored.value === null) {
|
|
257
|
-
return Result.ok({
|
|
258
|
-
agentName: null,
|
|
259
|
-
apiKeySource: "none",
|
|
260
|
-
loggedIn: false,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
return Result.ok({
|
|
264
|
-
agentName: stored.value.agentName ?? null,
|
|
265
|
-
apiKeySource: "credentialsFile",
|
|
266
|
-
loggedIn: true,
|
|
267
|
-
});
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
};
|
package/dist/services/info.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { CredentialsStore } from "../infra/credentials.js";
|
|
2
|
-
import type { HttpClient } from "../infra/http.js";
|
|
3
|
-
import type { AccountService } from "./account.js";
|
|
4
|
-
import type { BaseUrlSource, InfoPayload } from "../shared/types.js";
|
|
5
|
-
export interface InfoService {
|
|
6
|
-
collect(apiKeyOverride?: string): Promise<InfoPayload>;
|
|
7
|
-
}
|
|
8
|
-
interface InfoServiceDeps {
|
|
9
|
-
accountService: AccountService;
|
|
10
|
-
baseUrl: string;
|
|
11
|
-
baseUrlSource: BaseUrlSource;
|
|
12
|
-
configDir: string;
|
|
13
|
-
credentialsPath: string;
|
|
14
|
-
credentialsStore: CredentialsStore;
|
|
15
|
-
detectExecutionEnvironmentFn?: () => string;
|
|
16
|
-
envApiKey: string | undefined;
|
|
17
|
-
existsSyncFn?: (path: string) => boolean;
|
|
18
|
-
httpClient: HttpClient;
|
|
19
|
-
now?: () => number;
|
|
20
|
-
version: string;
|
|
21
|
-
}
|
|
22
|
-
export declare const createInfoService: (deps: InfoServiceDeps) => InfoService;
|
|
23
|
-
export {};
|
|
24
|
-
//# sourceMappingURL=info.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../src/services/info.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EACV,aAAa,EAGb,WAAW,EAEZ,MAAM,oBAAoB,CAAC;AAsB5B,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CACxD;AAED,UAAU,eAAe;IACvB,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,4BAA4B,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5C,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,UAAU,EAAE,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAmGD,eAAO,MAAM,iBAAiB,GAAI,MAAM,eAAe,KAAG,WAwQzD,CAAC"}
|
package/dist/services/info.js
DELETED
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { Result } from "better-result";
|
|
4
|
-
import { detectExecutionEnvironment } from "../infra/env.js";
|
|
5
|
-
const maskApiKey = (apiKey) => {
|
|
6
|
-
if (apiKey.length <= 8) {
|
|
7
|
-
return `*** (sha256:${createHash("sha256").update(apiKey).digest("hex").slice(0, 12)})`;
|
|
8
|
-
}
|
|
9
|
-
const start = apiKey.slice(0, 4);
|
|
10
|
-
const end = apiKey.slice(-4);
|
|
11
|
-
const fingerprint = createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
|
|
12
|
-
return `${start}...${end} (sha256:${fingerprint})`;
|
|
13
|
-
};
|
|
14
|
-
const resolveApiKey = (apiKeyOverride, envApiKey, credentialsStore) => {
|
|
15
|
-
if (typeof apiKeyOverride === "string" && apiKeyOverride.length > 0) {
|
|
16
|
-
return {
|
|
17
|
-
loadError: null,
|
|
18
|
-
source: "apiKeyOption",
|
|
19
|
-
value: apiKeyOverride,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
if (typeof envApiKey === "string" && envApiKey.length > 0) {
|
|
23
|
-
return {
|
|
24
|
-
loadError: null,
|
|
25
|
-
source: "env",
|
|
26
|
-
value: envApiKey,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
const storedCredentials = credentialsStore.load();
|
|
30
|
-
if (Result.isError(storedCredentials)) {
|
|
31
|
-
return {
|
|
32
|
-
loadError: storedCredentials.error.message,
|
|
33
|
-
source: "credentialsError",
|
|
34
|
-
value: null,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
if (storedCredentials.value !== null) {
|
|
38
|
-
return {
|
|
39
|
-
loadError: null,
|
|
40
|
-
source: "credentialsFile",
|
|
41
|
-
value: storedCredentials.value.apiKey,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
loadError: null,
|
|
46
|
-
source: "none",
|
|
47
|
-
value: null,
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
const nextActionByIssueCode = {
|
|
51
|
-
auth_failed: "Run `ogment auth login` to refresh credentials.",
|
|
52
|
-
credentials_load_failed: "Check file permissions and contents of `~/.config/ogment/credentials.json`.",
|
|
53
|
-
no_api_key: "Run `ogment auth login` or set `OGMENT_API_KEY`.",
|
|
54
|
-
unreachable: "Verify `OGMENT_BASE_URL` and network connectivity.",
|
|
55
|
-
};
|
|
56
|
-
const toSummary = (issues) => {
|
|
57
|
-
const nextActions = new Set();
|
|
58
|
-
for (const issue of issues) {
|
|
59
|
-
const nextAction = nextActionByIssueCode[issue.code];
|
|
60
|
-
if (nextAction !== undefined) {
|
|
61
|
-
nextActions.add(nextAction);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
nextActions.add("Run `ogment catalog` to inspect available servers.");
|
|
65
|
-
return {
|
|
66
|
-
issues: issues.map((issue) => issue.message),
|
|
67
|
-
nextActions: [...nextActions],
|
|
68
|
-
status: issues.length > 0 ? "warning" : "ok",
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
const emptyAccountErrorDetails = () => {
|
|
72
|
-
return {
|
|
73
|
-
errorCode: null,
|
|
74
|
-
errorHttpStatus: null,
|
|
75
|
-
errorMcpCode: null,
|
|
76
|
-
errorRaw: null,
|
|
77
|
-
errorRetryable: null,
|
|
78
|
-
errorSource: null,
|
|
79
|
-
};
|
|
80
|
-
};
|
|
81
|
-
export const createInfoService = (deps) => {
|
|
82
|
-
const detectEnvironment = deps.detectExecutionEnvironmentFn ?? detectExecutionEnvironment;
|
|
83
|
-
const existsSyncFn = deps.existsSyncFn ?? existsSync;
|
|
84
|
-
const now = deps.now ?? Date.now;
|
|
85
|
-
return {
|
|
86
|
-
collect: async (apiKeyOverride) => {
|
|
87
|
-
const apiKeyResolution = resolveApiKey(apiKeyOverride, deps.envApiKey, deps.credentialsStore);
|
|
88
|
-
const selectedApiKey = apiKeyResolution.value;
|
|
89
|
-
const credentialsFileExists = existsSyncFn(deps.credentialsPath);
|
|
90
|
-
const endpoint = `${deps.baseUrl}/api/v1/mcp-auth/me`;
|
|
91
|
-
const pingHeaders = typeof selectedApiKey === "string"
|
|
92
|
-
? {
|
|
93
|
-
Authorization: `Bearer ${selectedApiKey}`,
|
|
94
|
-
}
|
|
95
|
-
: undefined;
|
|
96
|
-
const pingStartedAt = now();
|
|
97
|
-
const pingResult = await deps.httpClient.request(endpoint, pingHeaders === undefined ? undefined : { headers: pingHeaders });
|
|
98
|
-
const pingElapsedMs = now() - pingStartedAt;
|
|
99
|
-
const ping = pingResult.status === "ok"
|
|
100
|
-
? {
|
|
101
|
-
endpoint,
|
|
102
|
-
error: null,
|
|
103
|
-
latencyMs: pingElapsedMs,
|
|
104
|
-
reachable: true,
|
|
105
|
-
status: pingResult.value.status,
|
|
106
|
-
statusText: pingResult.value.statusText,
|
|
107
|
-
}
|
|
108
|
-
: {
|
|
109
|
-
endpoint,
|
|
110
|
-
error: pingResult.error.message,
|
|
111
|
-
latencyMs: null,
|
|
112
|
-
reachable: false,
|
|
113
|
-
status: null,
|
|
114
|
-
statusText: null,
|
|
115
|
-
};
|
|
116
|
-
const collectAccount = async () => {
|
|
117
|
-
if (selectedApiKey === null) {
|
|
118
|
-
return {
|
|
119
|
-
...emptyAccountErrorDetails(),
|
|
120
|
-
errorType: null,
|
|
121
|
-
latencyMs: null,
|
|
122
|
-
message: "No API key available",
|
|
123
|
-
orgCount: null,
|
|
124
|
-
serverCount: null,
|
|
125
|
-
serverPaths: [],
|
|
126
|
-
status: "skipped",
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
const accountStartedAt = now();
|
|
130
|
-
const accountResult = await deps.accountService.fetchAccount(selectedApiKey);
|
|
131
|
-
const accountElapsedMs = now() - accountStartedAt;
|
|
132
|
-
if (accountResult.status === "ok") {
|
|
133
|
-
const servers = accountResult.value.orgs.flatMap((organization) => {
|
|
134
|
-
return organization.servers.map((server) => server.path);
|
|
135
|
-
});
|
|
136
|
-
return {
|
|
137
|
-
...emptyAccountErrorDetails(),
|
|
138
|
-
errorType: null,
|
|
139
|
-
latencyMs: accountElapsedMs,
|
|
140
|
-
message: null,
|
|
141
|
-
orgCount: accountResult.value.orgs.length,
|
|
142
|
-
serverCount: servers.length,
|
|
143
|
-
serverPaths: servers.slice(0, 5),
|
|
144
|
-
status: "success",
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
let normalizedStatus = "unexpected_error";
|
|
148
|
-
switch (accountResult.error._tag) {
|
|
149
|
-
case "AuthError": {
|
|
150
|
-
normalizedStatus = "auth_error";
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
case "RemoteRequestError": {
|
|
154
|
-
normalizedStatus = "remote_error";
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
case "ValidationError": {
|
|
158
|
-
normalizedStatus = "validation_error";
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
161
|
-
default: {
|
|
162
|
-
normalizedStatus = "unexpected_error";
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
...("code" in accountResult.error
|
|
168
|
-
? {
|
|
169
|
-
errorCode: String(accountResult.error.code),
|
|
170
|
-
}
|
|
171
|
-
: {
|
|
172
|
-
errorCode: null,
|
|
173
|
-
}),
|
|
174
|
-
...("httpStatus" in accountResult.error
|
|
175
|
-
? {
|
|
176
|
-
errorHttpStatus: typeof accountResult.error.httpStatus === "number"
|
|
177
|
-
? accountResult.error.httpStatus
|
|
178
|
-
: null,
|
|
179
|
-
}
|
|
180
|
-
: {
|
|
181
|
-
errorHttpStatus: null,
|
|
182
|
-
}),
|
|
183
|
-
...("mcpCode" in accountResult.error
|
|
184
|
-
? {
|
|
185
|
-
errorMcpCode: typeof accountResult.error.mcpCode === "number"
|
|
186
|
-
? accountResult.error.mcpCode
|
|
187
|
-
: null,
|
|
188
|
-
}
|
|
189
|
-
: {
|
|
190
|
-
errorMcpCode: null,
|
|
191
|
-
}),
|
|
192
|
-
...("raw" in accountResult.error
|
|
193
|
-
? {
|
|
194
|
-
errorRaw: accountResult.error.raw ?? null,
|
|
195
|
-
}
|
|
196
|
-
: {
|
|
197
|
-
errorRaw: null,
|
|
198
|
-
}),
|
|
199
|
-
...("retryable" in accountResult.error
|
|
200
|
-
? {
|
|
201
|
-
errorRetryable: typeof accountResult.error.retryable === "boolean"
|
|
202
|
-
? accountResult.error.retryable
|
|
203
|
-
: null,
|
|
204
|
-
}
|
|
205
|
-
: {
|
|
206
|
-
errorRetryable: null,
|
|
207
|
-
}),
|
|
208
|
-
...("source" in accountResult.error
|
|
209
|
-
? {
|
|
210
|
-
errorSource: typeof accountResult.error.source === "string"
|
|
211
|
-
? accountResult.error.source
|
|
212
|
-
: null,
|
|
213
|
-
}
|
|
214
|
-
: {
|
|
215
|
-
errorSource: null,
|
|
216
|
-
}),
|
|
217
|
-
errorType: accountResult.error._tag,
|
|
218
|
-
latencyMs: accountElapsedMs,
|
|
219
|
-
message: accountResult.error.message,
|
|
220
|
-
orgCount: null,
|
|
221
|
-
serverCount: null,
|
|
222
|
-
serverPaths: [],
|
|
223
|
-
status: normalizedStatus,
|
|
224
|
-
};
|
|
225
|
-
};
|
|
226
|
-
const account = await collectAccount();
|
|
227
|
-
const issues = [];
|
|
228
|
-
if (!ping.reachable) {
|
|
229
|
-
issues.push({
|
|
230
|
-
code: "unreachable",
|
|
231
|
-
message: `Could not reach ${deps.baseUrl}: ${ping.error ?? "unknown error"}`,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
if (apiKeyResolution.source === "none") {
|
|
235
|
-
issues.push({
|
|
236
|
-
code: "no_api_key",
|
|
237
|
-
message: "No API key found in --api-key, OGMENT_API_KEY, or credentials file.",
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
if (apiKeyResolution.source === "credentialsError" && apiKeyResolution.loadError !== null) {
|
|
241
|
-
issues.push({
|
|
242
|
-
code: "credentials_load_failed",
|
|
243
|
-
message: `Failed to load credentials file: ${apiKeyResolution.loadError}`,
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
if (account.status === "auth_error") {
|
|
247
|
-
issues.push({
|
|
248
|
-
code: "auth_failed",
|
|
249
|
-
message: `Authentication failed: ${account.message ?? "unknown auth error"}`,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
if (account.status === "remote_error") {
|
|
253
|
-
issues.push({
|
|
254
|
-
code: "remote_request_failed",
|
|
255
|
-
message: `Remote request failed: ${account.message ?? "unknown remote error"}`,
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
if (account.status === "validation_error") {
|
|
259
|
-
issues.push({
|
|
260
|
-
code: "response_validation_failed",
|
|
261
|
-
message: `Response validation failed: ${account.message ?? "unknown validation error"}`,
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
if (account.status === "unexpected_error") {
|
|
265
|
-
issues.push({
|
|
266
|
-
code: "unexpected_diagnostic_error",
|
|
267
|
-
message: `Unexpected diagnostic error: ${account.message ?? "unknown error"}`,
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
return {
|
|
271
|
-
auth: {
|
|
272
|
-
apiKeyPresent: selectedApiKey !== null,
|
|
273
|
-
apiKeyPreview: selectedApiKey === null ? null : maskApiKey(selectedApiKey),
|
|
274
|
-
apiKeySource: apiKeyResolution.source,
|
|
275
|
-
credentialsFileExists,
|
|
276
|
-
credentialsFileLoadError: apiKeyResolution.loadError,
|
|
277
|
-
},
|
|
278
|
-
config: {
|
|
279
|
-
baseUrl: deps.baseUrl,
|
|
280
|
-
baseUrlSource: deps.baseUrlSource,
|
|
281
|
-
configDir: deps.configDir,
|
|
282
|
-
credentialsPath: deps.credentialsPath,
|
|
283
|
-
},
|
|
284
|
-
documentation: {
|
|
285
|
-
configPrecedence: [
|
|
286
|
-
"--api-key option",
|
|
287
|
-
"OGMENT_API_KEY environment variable",
|
|
288
|
-
"~/.config/ogment/credentials.json",
|
|
289
|
-
"default base URL when OGMENT_BASE_URL is unset",
|
|
290
|
-
],
|
|
291
|
-
quickCommands: [
|
|
292
|
-
"ogment status",
|
|
293
|
-
"ogment auth login",
|
|
294
|
-
"ogment catalog",
|
|
295
|
-
"ogment catalog <server-id>",
|
|
296
|
-
"ogment catalog <server-id> <tool-name>",
|
|
297
|
-
"ogment invoke <server-id>/<tool-name> --input '{}'",
|
|
298
|
-
],
|
|
299
|
-
},
|
|
300
|
-
generatedAt: new Date(now()).toISOString(),
|
|
301
|
-
remote: {
|
|
302
|
-
account,
|
|
303
|
-
ping,
|
|
304
|
-
},
|
|
305
|
-
runtime: {
|
|
306
|
-
cliVersion: deps.version,
|
|
307
|
-
executionEnvironment: detectEnvironment(),
|
|
308
|
-
nodeVersion: process.version,
|
|
309
|
-
platform: process.platform,
|
|
310
|
-
processArch: process.arch,
|
|
311
|
-
},
|
|
312
|
-
summary: toSummary(issues),
|
|
313
|
-
};
|
|
314
|
-
},
|
|
315
|
-
};
|
|
316
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { RemoteRequestError } from "../shared/errors.js";
|
|
2
|
-
interface CreateRemoteRequestErrorOptions {
|
|
3
|
-
cause: unknown;
|
|
4
|
-
message: string;
|
|
5
|
-
operation: string;
|
|
6
|
-
}
|
|
7
|
-
export declare const createRemoteRequestErrorFromMcpCause: (options: CreateRemoteRequestErrorOptions) => RemoteRequestError;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=mcp-error-mapping.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-error-mapping.d.ts","sourceRoot":"","sources":["../../src/services/mcp-error-mapping.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAazD,UAAU,+BAA+B;IACvC,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AA0ID,eAAO,MAAM,oCAAoC,GAC/C,SAAS,+BAA+B,KACvC,kBAaF,CAAC"}
|