@ogment-ai/cli 0.8.2 → 0.9.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 +2 -2
- package/dist/cli.js +1081 -620
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import { createReadStream, existsSync, lstatSync, mkdirSync, readFile, readFileSync, readdir, realpathSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { closeSync, createReadStream, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFile, readFileSync, readdir, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import { errorMonitor } from "node:events";
|
|
6
6
|
import { execFile, execSync } from "node:child_process";
|
|
7
7
|
import { dirname, join, posix, sep } from "node:path";
|
|
8
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
8
9
|
import * as os from "node:os";
|
|
9
10
|
import { homedir } from "node:os";
|
|
10
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
11
11
|
import { readFile as readFile$1 } from "node:fs/promises";
|
|
12
12
|
import * as moduleModule from "module";
|
|
13
13
|
import { isMainThread, threadId } from "worker_threads";
|
|
@@ -3806,409 +3806,6 @@ const Result = {
|
|
|
3806
3806
|
flatten: flatten$1
|
|
3807
3807
|
};
|
|
3808
3808
|
|
|
3809
|
-
//#endregion
|
|
3810
|
-
//#region src/shared/error-codes.ts
|
|
3811
|
-
const ERROR_CATEGORY = {
|
|
3812
|
-
auth: "auth",
|
|
3813
|
-
authorization: "authorization",
|
|
3814
|
-
contractMismatch: "contract_mismatch",
|
|
3815
|
-
internal: "internal",
|
|
3816
|
-
notFound: "not_found",
|
|
3817
|
-
rateLimit: "rate_limit",
|
|
3818
|
-
remote: "remote",
|
|
3819
|
-
transport: "transport",
|
|
3820
|
-
validation: "validation"
|
|
3821
|
-
};
|
|
3822
|
-
const ERROR_CODE = {
|
|
3823
|
-
authDeviceExpired: "AUTH_DEVICE_EXPIRED",
|
|
3824
|
-
authDevicePending: "AUTH_DEVICE_PENDING",
|
|
3825
|
-
authInvalidCredentials: "AUTH_INVALID_CREDENTIALS",
|
|
3826
|
-
authRequired: "AUTH_REQUIRED",
|
|
3827
|
-
contractVersionUnsupported: "CONTRACT_VERSION_UNSUPPORTED",
|
|
3828
|
-
internalUnexpected: "INTERNAL_UNEXPECTED",
|
|
3829
|
-
remoteRateLimited: "REMOTE_RATE_LIMITED",
|
|
3830
|
-
remoteUnavailable: "REMOTE_UNAVAILABLE",
|
|
3831
|
-
toolInputSchemaViolation: "TOOL_INPUT_SCHEMA_VIOLATION",
|
|
3832
|
-
toolNotFound: "TOOL_NOT_FOUND",
|
|
3833
|
-
transportRequestFailed: "TRANSPORT_REQUEST_FAILED",
|
|
3834
|
-
validationInvalidInput: "VALIDATION_INVALID_INPUT"
|
|
3835
|
-
};
|
|
3836
|
-
const staticErrorCodes = new Set(Object.values(ERROR_CODE));
|
|
3837
|
-
const isStaticErrorCode = (code) => {
|
|
3838
|
-
return staticErrorCodes.has(code);
|
|
3839
|
-
};
|
|
3840
|
-
|
|
3841
|
-
//#endregion
|
|
3842
|
-
//#region src/shared/json.ts
|
|
3843
|
-
const isJsonValue = (value) => {
|
|
3844
|
-
if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") return true;
|
|
3845
|
-
if (Array.isArray(value)) return value.every((item) => isJsonValue(item));
|
|
3846
|
-
if (typeof value === "object") {
|
|
3847
|
-
if (value === null) return false;
|
|
3848
|
-
return Object.values(value).every((item) => isJsonValue(item));
|
|
3849
|
-
}
|
|
3850
|
-
return false;
|
|
3851
|
-
};
|
|
3852
|
-
const isJsonObject = (value) => {
|
|
3853
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && isJsonValue(value);
|
|
3854
|
-
};
|
|
3855
|
-
const toJsonValue = (value) => {
|
|
3856
|
-
if (isJsonValue(value)) return value;
|
|
3857
|
-
try {
|
|
3858
|
-
const serialized = JSON.stringify(value);
|
|
3859
|
-
if (serialized === void 0) return "[unserializable]";
|
|
3860
|
-
const parsed = JSON.parse(serialized);
|
|
3861
|
-
if (isJsonValue(parsed)) return parsed;
|
|
3862
|
-
return "[unserializable]";
|
|
3863
|
-
} catch {
|
|
3864
|
-
return "[unserializable]";
|
|
3865
|
-
}
|
|
3866
|
-
};
|
|
3867
|
-
|
|
3868
|
-
//#endregion
|
|
3869
|
-
//#region src/shared/recovery.ts
|
|
3870
|
-
const defaultRecoveryByCode = {
|
|
3871
|
-
[ERROR_CODE.authDeviceExpired]: {
|
|
3872
|
-
command: "ogment auth login",
|
|
3873
|
-
reason: "Device code expired; restart login to continue.",
|
|
3874
|
-
title: "Restart login",
|
|
3875
|
-
when: "immediate"
|
|
3876
|
-
},
|
|
3877
|
-
[ERROR_CODE.authDevicePending]: {
|
|
3878
|
-
command: "ogment auth status",
|
|
3879
|
-
reason: "Authorization is pending; check whether credentials are now available.",
|
|
3880
|
-
title: "Check auth status",
|
|
3881
|
-
when: "immediate"
|
|
3882
|
-
},
|
|
3883
|
-
[ERROR_CODE.authInvalidCredentials]: {
|
|
3884
|
-
command: "ogment auth logout",
|
|
3885
|
-
reason: "Credentials are invalid; clear local state before re-authenticating.",
|
|
3886
|
-
title: "Reset auth state",
|
|
3887
|
-
when: "immediate"
|
|
3888
|
-
},
|
|
3889
|
-
[ERROR_CODE.authRequired]: {
|
|
3890
|
-
command: "ogment auth login",
|
|
3891
|
-
reason: "Authentication is required before catalog or invoke commands can run.",
|
|
3892
|
-
title: "Authenticate",
|
|
3893
|
-
when: "immediate"
|
|
3894
|
-
},
|
|
3895
|
-
[ERROR_CODE.contractVersionUnsupported]: {
|
|
3896
|
-
command: "ogment --version",
|
|
3897
|
-
reason: "Contract mismatch detected; verify CLI version and upgrade before retrying.",
|
|
3898
|
-
title: "Inspect CLI version",
|
|
3899
|
-
when: "immediate"
|
|
3900
|
-
},
|
|
3901
|
-
[ERROR_CODE.internalUnexpected]: {
|
|
3902
|
-
command: "ogment status",
|
|
3903
|
-
reason: "Unexpected failure detected; run diagnostics before retrying the workflow.",
|
|
3904
|
-
title: "Inspect diagnostics",
|
|
3905
|
-
when: "immediate"
|
|
3906
|
-
},
|
|
3907
|
-
[ERROR_CODE.remoteRateLimited]: {
|
|
3908
|
-
command: "ogment status",
|
|
3909
|
-
reason: "Remote rate limit detected; check diagnostics before retrying.",
|
|
3910
|
-
title: "Inspect diagnostics",
|
|
3911
|
-
when: "immediate"
|
|
3912
|
-
},
|
|
3913
|
-
[ERROR_CODE.remoteUnavailable]: {
|
|
3914
|
-
command: "ogment status",
|
|
3915
|
-
reason: "Remote service is unavailable; check connectivity and endpoint health.",
|
|
3916
|
-
title: "Inspect diagnostics",
|
|
3917
|
-
when: "immediate"
|
|
3918
|
-
},
|
|
3919
|
-
[ERROR_CODE.toolInputSchemaViolation]: {
|
|
3920
|
-
command: "ogment catalog",
|
|
3921
|
-
reason: "Tool contract did not validate; inspect available servers and tool metadata.",
|
|
3922
|
-
title: "Inspect catalog",
|
|
3923
|
-
when: "immediate"
|
|
3924
|
-
},
|
|
3925
|
-
[ERROR_CODE.toolNotFound]: {
|
|
3926
|
-
command: "ogment catalog",
|
|
3927
|
-
reason: "Requested tool could not be found; rediscover servers and tools.",
|
|
3928
|
-
title: "Rediscover tools",
|
|
3929
|
-
when: "immediate"
|
|
3930
|
-
},
|
|
3931
|
-
[ERROR_CODE.transportRequestFailed]: {
|
|
3932
|
-
command: "ogment status",
|
|
3933
|
-
reason: "Transport request failed; inspect connectivity diagnostics.",
|
|
3934
|
-
title: "Inspect diagnostics",
|
|
3935
|
-
when: "immediate"
|
|
3936
|
-
},
|
|
3937
|
-
[ERROR_CODE.validationInvalidInput]: {
|
|
3938
|
-
command: "ogment --help",
|
|
3939
|
-
reason: "Input validation failed; inspect canonical command syntax.",
|
|
3940
|
-
title: "Show command help",
|
|
3941
|
-
when: "immediate"
|
|
3942
|
-
}
|
|
3943
|
-
};
|
|
3944
|
-
const dynamicRecoveryForCode = (code) => {
|
|
3945
|
-
if (code.startsWith("HTTP_")) return {
|
|
3946
|
-
command: "ogment status",
|
|
3947
|
-
reason: `HTTP error ${code} detected; inspect diagnostics and remote connectivity.`,
|
|
3948
|
-
title: "Inspect diagnostics",
|
|
3949
|
-
when: "immediate"
|
|
3950
|
-
};
|
|
3951
|
-
if (code.startsWith("MCP_")) return {
|
|
3952
|
-
command: "ogment status",
|
|
3953
|
-
reason: `MCP error ${code} detected; inspect diagnostics and remote server state.`,
|
|
3954
|
-
title: "Inspect diagnostics",
|
|
3955
|
-
when: "immediate"
|
|
3956
|
-
};
|
|
3957
|
-
};
|
|
3958
|
-
const hasPlaceholderTokens = (command) => {
|
|
3959
|
-
return command.includes("<") || command.includes(">");
|
|
3960
|
-
};
|
|
3961
|
-
const sanitizedCommand = (command) => {
|
|
3962
|
-
if (typeof command !== "string") return;
|
|
3963
|
-
if (hasPlaceholderTokens(command)) return;
|
|
3964
|
-
return command;
|
|
3965
|
-
};
|
|
3966
|
-
const normalizeRecovery = (code, override) => {
|
|
3967
|
-
const fallback = (isStaticErrorCode(code) ? defaultRecoveryByCode[code] : void 0) ?? dynamicRecoveryForCode(code);
|
|
3968
|
-
if (fallback === void 0) return;
|
|
3969
|
-
const overrideCommand = sanitizedCommand(override?.command);
|
|
3970
|
-
const command = overrideCommand ?? fallback.command;
|
|
3971
|
-
const usesCustomCommand = overrideCommand !== void 0 && overrideCommand !== fallback.command;
|
|
3972
|
-
return {
|
|
3973
|
-
command,
|
|
3974
|
-
id: override?.id ?? `recover_${code.toLowerCase()}`,
|
|
3975
|
-
reason: override?.reason ?? (usesCustomCommand ? `Suggested recovery for error ${code}.` : fallback.reason),
|
|
3976
|
-
title: override?.title ?? (usesCustomCommand ? "Run suggested command" : fallback.title),
|
|
3977
|
-
when: override?.when ?? fallback.when
|
|
3978
|
-
};
|
|
3979
|
-
};
|
|
3980
|
-
|
|
3981
|
-
//#endregion
|
|
3982
|
-
//#region src/shared/errors.ts
|
|
3983
|
-
const ValidationErrorBase = TaggedError("ValidationError")();
|
|
3984
|
-
var ValidationError = class extends ValidationErrorBase {
|
|
3985
|
-
category = ERROR_CATEGORY.validation;
|
|
3986
|
-
code;
|
|
3987
|
-
recovery;
|
|
3988
|
-
retryable = false;
|
|
3989
|
-
constructor(payload) {
|
|
3990
|
-
super({
|
|
3991
|
-
message: payload.message,
|
|
3992
|
-
...payload.details === void 0 ? {} : { details: payload.details }
|
|
3993
|
-
});
|
|
3994
|
-
this.code = payload.code ?? ERROR_CODE.validationInvalidInput;
|
|
3995
|
-
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
3996
|
-
}
|
|
3997
|
-
};
|
|
3998
|
-
const AuthErrorBase = TaggedError("AuthError")();
|
|
3999
|
-
var AuthError = class extends AuthErrorBase {
|
|
4000
|
-
category = ERROR_CATEGORY.auth;
|
|
4001
|
-
code;
|
|
4002
|
-
recovery;
|
|
4003
|
-
retryable;
|
|
4004
|
-
constructor(payload) {
|
|
4005
|
-
super({
|
|
4006
|
-
message: payload.message,
|
|
4007
|
-
...payload.details === void 0 ? {} : { details: payload.details }
|
|
4008
|
-
});
|
|
4009
|
-
this.code = payload.code ?? ERROR_CODE.authRequired;
|
|
4010
|
-
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
4011
|
-
this.retryable = payload.retryable ?? this.code === ERROR_CODE.authDeviceExpired;
|
|
4012
|
-
}
|
|
4013
|
-
};
|
|
4014
|
-
const NotFoundErrorBase = TaggedError("NotFoundError")();
|
|
4015
|
-
var NotFoundError = class extends NotFoundErrorBase {
|
|
4016
|
-
category = ERROR_CATEGORY.notFound;
|
|
4017
|
-
code;
|
|
4018
|
-
recovery;
|
|
4019
|
-
retryable = false;
|
|
4020
|
-
constructor(payload) {
|
|
4021
|
-
super({
|
|
4022
|
-
message: payload.message,
|
|
4023
|
-
resource: payload.resource
|
|
4024
|
-
});
|
|
4025
|
-
this.code = payload.code ?? ERROR_CODE.toolNotFound;
|
|
4026
|
-
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
4027
|
-
}
|
|
4028
|
-
};
|
|
4029
|
-
const RemoteRequestErrorBase = TaggedError("RemoteRequestError")();
|
|
4030
|
-
const parseHttpCode = (code) => {
|
|
4031
|
-
if (!code.startsWith("HTTP_")) return;
|
|
4032
|
-
const parsed = Number.parseInt(code.slice(5), 10);
|
|
4033
|
-
if (!Number.isFinite(parsed)) return;
|
|
4034
|
-
return parsed;
|
|
4035
|
-
};
|
|
4036
|
-
const resolveRemoteErrorCode = (payload, diagnostics) => {
|
|
4037
|
-
if (payload.code !== void 0) return payload.code;
|
|
4038
|
-
if (typeof diagnostics.httpStatus === "number") return `HTTP_${diagnostics.httpStatus}`;
|
|
4039
|
-
if (typeof diagnostics.mcpCode === "number") return `MCP_${diagnostics.mcpCode}`;
|
|
4040
|
-
return ERROR_CODE.transportRequestFailed;
|
|
4041
|
-
};
|
|
4042
|
-
const resolveRemoteDiagnostics = (payload) => {
|
|
4043
|
-
const source = payload.source ?? payload.diagnostics?.source ?? "network";
|
|
4044
|
-
const httpStatus = payload.httpStatus ?? payload.status ?? payload.diagnostics?.httpStatus;
|
|
4045
|
-
const mcpCode = payload.mcpCode ?? payload.diagnostics?.mcpCode;
|
|
4046
|
-
const mcpData = payload.mcpData ?? payload.diagnostics?.mcpData;
|
|
4047
|
-
const operation = payload.operation ?? payload.diagnostics?.operation;
|
|
4048
|
-
const raw = payload.raw ?? payload.diagnostics?.raw;
|
|
4049
|
-
return {
|
|
4050
|
-
source,
|
|
4051
|
-
...httpStatus === void 0 ? {} : { httpStatus },
|
|
4052
|
-
...mcpCode === void 0 ? {} : { mcpCode },
|
|
4053
|
-
...mcpData === void 0 ? {} : { mcpData },
|
|
4054
|
-
...operation === void 0 ? {} : { operation },
|
|
4055
|
-
...raw === void 0 ? {} : { raw }
|
|
4056
|
-
};
|
|
4057
|
-
};
|
|
4058
|
-
const resolveRetryableRemoteError = (diagnostics, code) => {
|
|
4059
|
-
const httpStatus = diagnostics.httpStatus ?? parseHttpCode(code);
|
|
4060
|
-
if (typeof httpStatus === "number") return httpStatus === 429 || httpStatus >= 500;
|
|
4061
|
-
if (typeof diagnostics.mcpCode === "number") return diagnostics.mcpCode === -32001;
|
|
4062
|
-
return diagnostics.source === "network";
|
|
4063
|
-
};
|
|
4064
|
-
const resolveRemoteCategory = (code, diagnostics) => {
|
|
4065
|
-
const httpStatus = diagnostics.httpStatus ?? parseHttpCode(code);
|
|
4066
|
-
if (code === ERROR_CODE.remoteRateLimited || httpStatus === 429) return ERROR_CATEGORY.rateLimit;
|
|
4067
|
-
return ERROR_CATEGORY.remote;
|
|
4068
|
-
};
|
|
4069
|
-
var RemoteRequestError = class extends RemoteRequestErrorBase {
|
|
4070
|
-
category;
|
|
4071
|
-
code;
|
|
4072
|
-
diagnostics;
|
|
4073
|
-
httpStatus;
|
|
4074
|
-
recovery;
|
|
4075
|
-
retryable;
|
|
4076
|
-
constructor(payload) {
|
|
4077
|
-
const diagnostics = resolveRemoteDiagnostics(payload);
|
|
4078
|
-
const code = resolveRemoteErrorCode(payload, diagnostics);
|
|
4079
|
-
const status = diagnostics.httpStatus;
|
|
4080
|
-
super({
|
|
4081
|
-
message: payload.message,
|
|
4082
|
-
...payload.body === void 0 ? {} : { body: payload.body },
|
|
4083
|
-
...status === void 0 ? {} : { status },
|
|
4084
|
-
source: diagnostics.source,
|
|
4085
|
-
...diagnostics.mcpCode === void 0 ? {} : { mcpCode: diagnostics.mcpCode },
|
|
4086
|
-
...diagnostics.mcpData === void 0 ? {} : { mcpData: diagnostics.mcpData },
|
|
4087
|
-
...diagnostics.operation === void 0 ? {} : { operation: diagnostics.operation },
|
|
4088
|
-
...diagnostics.raw === void 0 ? {} : { raw: diagnostics.raw }
|
|
4089
|
-
});
|
|
4090
|
-
this.code = code;
|
|
4091
|
-
this.category = resolveRemoteCategory(code, diagnostics);
|
|
4092
|
-
this.diagnostics = diagnostics;
|
|
4093
|
-
this.httpStatus = diagnostics.httpStatus;
|
|
4094
|
-
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
4095
|
-
this.retryable = payload.retryable ?? resolveRetryableRemoteError(diagnostics, code);
|
|
4096
|
-
}
|
|
4097
|
-
};
|
|
4098
|
-
const ContractMismatchErrorBase = TaggedError("ContractMismatchError")();
|
|
4099
|
-
var ContractMismatchError = class extends ContractMismatchErrorBase {
|
|
4100
|
-
category = ERROR_CATEGORY.contractMismatch;
|
|
4101
|
-
code;
|
|
4102
|
-
recovery;
|
|
4103
|
-
retryable = false;
|
|
4104
|
-
constructor(payload) {
|
|
4105
|
-
super({
|
|
4106
|
-
message: payload.message,
|
|
4107
|
-
...payload.details === void 0 ? {} : { details: payload.details }
|
|
4108
|
-
});
|
|
4109
|
-
this.code = payload.code ?? ERROR_CODE.contractVersionUnsupported;
|
|
4110
|
-
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
4111
|
-
}
|
|
4112
|
-
};
|
|
4113
|
-
const UnexpectedErrorBase = TaggedError("UnexpectedError")();
|
|
4114
|
-
var UnexpectedError = class extends UnexpectedErrorBase {
|
|
4115
|
-
category = ERROR_CATEGORY.internal;
|
|
4116
|
-
code;
|
|
4117
|
-
recovery;
|
|
4118
|
-
retryable = false;
|
|
4119
|
-
constructor(payload) {
|
|
4120
|
-
super({
|
|
4121
|
-
message: payload.message,
|
|
4122
|
-
...payload.cause === void 0 ? {} : { cause: payload.cause }
|
|
4123
|
-
});
|
|
4124
|
-
this.code = payload.code ?? ERROR_CODE.internalUnexpected;
|
|
4125
|
-
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
4126
|
-
}
|
|
4127
|
-
};
|
|
4128
|
-
const withIfDefined = (base, key, value) => {
|
|
4129
|
-
if (value === void 0) return base;
|
|
4130
|
-
return {
|
|
4131
|
-
...base,
|
|
4132
|
-
[key]: value
|
|
4133
|
-
};
|
|
4134
|
-
};
|
|
4135
|
-
const hasDetails = (error) => {
|
|
4136
|
-
return error._tag === "AuthError" || error._tag === "ValidationError";
|
|
4137
|
-
};
|
|
4138
|
-
const appErrorMeta = (error) => {
|
|
4139
|
-
return {
|
|
4140
|
-
category: error.category,
|
|
4141
|
-
code: error.code,
|
|
4142
|
-
recovery: error.recovery,
|
|
4143
|
-
retryable: error.retryable
|
|
4144
|
-
};
|
|
4145
|
-
};
|
|
4146
|
-
const appErrorDiagnostics = (error) => {
|
|
4147
|
-
if (error._tag === "RemoteRequestError") {
|
|
4148
|
-
let diagnostics = { source: error.source };
|
|
4149
|
-
diagnostics = withIfDefined(diagnostics, "httpStatus", error.httpStatus);
|
|
4150
|
-
diagnostics = withIfDefined(diagnostics, "mcpCode", error.mcpCode);
|
|
4151
|
-
diagnostics = withIfDefined(diagnostics, "mcpData", error.mcpData);
|
|
4152
|
-
diagnostics = withIfDefined(diagnostics, "operation", error.operation);
|
|
4153
|
-
diagnostics = withIfDefined(diagnostics, "raw", error.raw);
|
|
4154
|
-
diagnostics = withIfDefined(diagnostics, "body", error.body);
|
|
4155
|
-
return diagnostics;
|
|
4156
|
-
}
|
|
4157
|
-
if (error._tag === "NotFoundError") return { resource: error.resource };
|
|
4158
|
-
if (error._tag === "UnexpectedError") {
|
|
4159
|
-
if (error.cause === void 0) return {};
|
|
4160
|
-
return { cause: toJsonValue(error.cause) };
|
|
4161
|
-
}
|
|
4162
|
-
if (hasDetails(error) && typeof error.details === "string" && error.details.length > 0) return { details: error.details };
|
|
4163
|
-
return {};
|
|
4164
|
-
};
|
|
4165
|
-
const appErrorTitle = (error) => {
|
|
4166
|
-
if (error._tag === "UnexpectedError") return "Unexpected CLI error";
|
|
4167
|
-
if (error._tag === "ContractMismatchError") return "Contract version unsupported";
|
|
4168
|
-
return error.message;
|
|
4169
|
-
};
|
|
4170
|
-
const formatAppError = (error) => {
|
|
4171
|
-
if (error._tag === "NotFoundError") return `${error.message} (${error.resource})`;
|
|
4172
|
-
if (error._tag === "RemoteRequestError" && typeof error.httpStatus === "number") return `${error.message} (status ${error.httpStatus})`;
|
|
4173
|
-
if (hasDetails(error) && typeof error.details === "string" && error.details.length > 0) return `${error.message}: ${error.details}`;
|
|
4174
|
-
return error.message;
|
|
4175
|
-
};
|
|
4176
|
-
|
|
4177
|
-
//#endregion
|
|
4178
|
-
//#region src/shared/guards.ts
|
|
4179
|
-
const formatIssues = (issues) => {
|
|
4180
|
-
return issues.map((issue) => {
|
|
4181
|
-
if (issue.path.length === 0) return issue.message;
|
|
4182
|
-
return `${issue.path.filter((value) => typeof value === "number" || typeof value === "string").join(".")}: ${issue.message}`;
|
|
4183
|
-
}).join("; ");
|
|
4184
|
-
};
|
|
4185
|
-
const parseWithSchema = (schema, payload, context, options = {}) => {
|
|
4186
|
-
const parsed = schema.safeParse(payload);
|
|
4187
|
-
if (!parsed.success) return Result.err(new ValidationError({
|
|
4188
|
-
details: formatIssues(parsed.error.issues),
|
|
4189
|
-
message: `Invalid ${context}`,
|
|
4190
|
-
...options.code === void 0 ? {} : { code: options.code },
|
|
4191
|
-
...options.recovery === void 0 ? {} : { recovery: options.recovery }
|
|
4192
|
-
}));
|
|
4193
|
-
return Result.ok(parsed.data);
|
|
4194
|
-
};
|
|
4195
|
-
const parseJsonValue = (raw, context) => {
|
|
4196
|
-
return Result.try({
|
|
4197
|
-
catch: () => {
|
|
4198
|
-
return new ValidationError({
|
|
4199
|
-
details: raw,
|
|
4200
|
-
message: `Invalid JSON in ${context}`
|
|
4201
|
-
});
|
|
4202
|
-
},
|
|
4203
|
-
try: () => {
|
|
4204
|
-
return toJsonValue(JSON.parse(raw));
|
|
4205
|
-
}
|
|
4206
|
-
});
|
|
4207
|
-
};
|
|
4208
|
-
const isRecord$2 = (value) => {
|
|
4209
|
-
return typeof value === "object" && value !== null;
|
|
4210
|
-
};
|
|
4211
|
-
|
|
4212
3809
|
//#endregion
|
|
4213
3810
|
//#region ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/core.js
|
|
4214
3811
|
/** A special constant with type `never` */
|
|
@@ -8336,6 +7933,440 @@ function number(params) {
|
|
|
8336
7933
|
return _coercedNumber(ZodNumber, params);
|
|
8337
7934
|
}
|
|
8338
7935
|
|
|
7936
|
+
//#endregion
|
|
7937
|
+
//#region src/shared/error-codes.ts
|
|
7938
|
+
const ERROR_CATEGORY = {
|
|
7939
|
+
auth: "auth",
|
|
7940
|
+
authorization: "authorization",
|
|
7941
|
+
contractMismatch: "contract_mismatch",
|
|
7942
|
+
internal: "internal",
|
|
7943
|
+
notFound: "not_found",
|
|
7944
|
+
rateLimit: "rate_limit",
|
|
7945
|
+
remote: "remote",
|
|
7946
|
+
transport: "transport",
|
|
7947
|
+
updateRequired: "update_required",
|
|
7948
|
+
validation: "validation"
|
|
7949
|
+
};
|
|
7950
|
+
const ERROR_CODE = {
|
|
7951
|
+
authDeviceExpired: "AUTH_DEVICE_EXPIRED",
|
|
7952
|
+
authDevicePending: "AUTH_DEVICE_PENDING",
|
|
7953
|
+
authInvalidCredentials: "AUTH_INVALID_CREDENTIALS",
|
|
7954
|
+
authRequired: "AUTH_REQUIRED",
|
|
7955
|
+
cliUpdateRequired: "CLI_UPDATE_REQUIRED",
|
|
7956
|
+
contractVersionUnsupported: "CONTRACT_VERSION_UNSUPPORTED",
|
|
7957
|
+
internalUnexpected: "INTERNAL_UNEXPECTED",
|
|
7958
|
+
remoteRateLimited: "REMOTE_RATE_LIMITED",
|
|
7959
|
+
remoteUnavailable: "REMOTE_UNAVAILABLE",
|
|
7960
|
+
toolInputSchemaViolation: "TOOL_INPUT_SCHEMA_VIOLATION",
|
|
7961
|
+
toolNotFound: "TOOL_NOT_FOUND",
|
|
7962
|
+
transportRequestFailed: "TRANSPORT_REQUEST_FAILED",
|
|
7963
|
+
validationInvalidInput: "VALIDATION_INVALID_INPUT"
|
|
7964
|
+
};
|
|
7965
|
+
const staticErrorCodes = new Set(Object.values(ERROR_CODE));
|
|
7966
|
+
const isStaticErrorCode = (code) => {
|
|
7967
|
+
return staticErrorCodes.has(code);
|
|
7968
|
+
};
|
|
7969
|
+
|
|
7970
|
+
//#endregion
|
|
7971
|
+
//#region src/shared/json.ts
|
|
7972
|
+
const isJsonValue = (value) => {
|
|
7973
|
+
if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") return true;
|
|
7974
|
+
if (Array.isArray(value)) return value.every((item) => isJsonValue(item));
|
|
7975
|
+
if (typeof value === "object") {
|
|
7976
|
+
if (value === null) return false;
|
|
7977
|
+
return Object.values(value).every((item) => isJsonValue(item));
|
|
7978
|
+
}
|
|
7979
|
+
return false;
|
|
7980
|
+
};
|
|
7981
|
+
const isJsonObject = (value) => {
|
|
7982
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && isJsonValue(value);
|
|
7983
|
+
};
|
|
7984
|
+
const toJsonValue = (value) => {
|
|
7985
|
+
if (isJsonValue(value)) return value;
|
|
7986
|
+
try {
|
|
7987
|
+
const serialized = JSON.stringify(value);
|
|
7988
|
+
if (serialized === void 0) return "[unserializable]";
|
|
7989
|
+
const parsed = JSON.parse(serialized);
|
|
7990
|
+
if (isJsonValue(parsed)) return parsed;
|
|
7991
|
+
return "[unserializable]";
|
|
7992
|
+
} catch {
|
|
7993
|
+
return "[unserializable]";
|
|
7994
|
+
}
|
|
7995
|
+
};
|
|
7996
|
+
|
|
7997
|
+
//#endregion
|
|
7998
|
+
//#region src/shared/recovery.ts
|
|
7999
|
+
const defaultRecoveryByCode = {
|
|
8000
|
+
[ERROR_CODE.authDeviceExpired]: {
|
|
8001
|
+
command: "ogment auth login",
|
|
8002
|
+
reason: "Device code expired; restart login to continue.",
|
|
8003
|
+
title: "Restart login",
|
|
8004
|
+
when: "immediate"
|
|
8005
|
+
},
|
|
8006
|
+
[ERROR_CODE.authDevicePending]: {
|
|
8007
|
+
command: "ogment auth status",
|
|
8008
|
+
reason: "Authorization is pending; check whether credentials are now available.",
|
|
8009
|
+
title: "Check auth status",
|
|
8010
|
+
when: "immediate"
|
|
8011
|
+
},
|
|
8012
|
+
[ERROR_CODE.authInvalidCredentials]: {
|
|
8013
|
+
command: "ogment auth logout",
|
|
8014
|
+
reason: "Credentials are invalid; clear local state before re-authenticating.",
|
|
8015
|
+
title: "Reset auth state",
|
|
8016
|
+
when: "immediate"
|
|
8017
|
+
},
|
|
8018
|
+
[ERROR_CODE.authRequired]: {
|
|
8019
|
+
command: "ogment auth login",
|
|
8020
|
+
reason: "Authentication is required before catalog or invoke commands can run.",
|
|
8021
|
+
title: "Authenticate",
|
|
8022
|
+
when: "immediate"
|
|
8023
|
+
},
|
|
8024
|
+
[ERROR_CODE.cliUpdateRequired]: {
|
|
8025
|
+
command: "npm i -g @ogment-ai/cli",
|
|
8026
|
+
reason: "A newer Ogment CLI release is required before the requested command can run.",
|
|
8027
|
+
title: "Update CLI",
|
|
8028
|
+
when: "immediate"
|
|
8029
|
+
},
|
|
8030
|
+
[ERROR_CODE.contractVersionUnsupported]: {
|
|
8031
|
+
command: "ogment --version",
|
|
8032
|
+
reason: "Contract mismatch detected; verify CLI version and upgrade before retrying.",
|
|
8033
|
+
title: "Inspect CLI version",
|
|
8034
|
+
when: "immediate"
|
|
8035
|
+
},
|
|
8036
|
+
[ERROR_CODE.internalUnexpected]: {
|
|
8037
|
+
command: "ogment status",
|
|
8038
|
+
reason: "Unexpected failure detected; run diagnostics before retrying the workflow.",
|
|
8039
|
+
title: "Inspect diagnostics",
|
|
8040
|
+
when: "immediate"
|
|
8041
|
+
},
|
|
8042
|
+
[ERROR_CODE.remoteRateLimited]: {
|
|
8043
|
+
command: "ogment status",
|
|
8044
|
+
reason: "Remote rate limit detected; check diagnostics before retrying.",
|
|
8045
|
+
title: "Inspect diagnostics",
|
|
8046
|
+
when: "immediate"
|
|
8047
|
+
},
|
|
8048
|
+
[ERROR_CODE.remoteUnavailable]: {
|
|
8049
|
+
command: "ogment status",
|
|
8050
|
+
reason: "Remote service is unavailable; check connectivity and endpoint health.",
|
|
8051
|
+
title: "Inspect diagnostics",
|
|
8052
|
+
when: "immediate"
|
|
8053
|
+
},
|
|
8054
|
+
[ERROR_CODE.toolInputSchemaViolation]: {
|
|
8055
|
+
command: "ogment catalog",
|
|
8056
|
+
reason: "Tool contract did not validate; inspect available servers and tool metadata.",
|
|
8057
|
+
title: "Inspect catalog",
|
|
8058
|
+
when: "immediate"
|
|
8059
|
+
},
|
|
8060
|
+
[ERROR_CODE.toolNotFound]: {
|
|
8061
|
+
command: "ogment catalog",
|
|
8062
|
+
reason: "Requested tool could not be found; rediscover servers and tools.",
|
|
8063
|
+
title: "Rediscover tools",
|
|
8064
|
+
when: "immediate"
|
|
8065
|
+
},
|
|
8066
|
+
[ERROR_CODE.transportRequestFailed]: {
|
|
8067
|
+
command: "ogment status",
|
|
8068
|
+
reason: "Transport request failed; inspect connectivity diagnostics.",
|
|
8069
|
+
title: "Inspect diagnostics",
|
|
8070
|
+
when: "immediate"
|
|
8071
|
+
},
|
|
8072
|
+
[ERROR_CODE.validationInvalidInput]: {
|
|
8073
|
+
command: "ogment --help",
|
|
8074
|
+
reason: "Input validation failed; inspect canonical command syntax.",
|
|
8075
|
+
title: "Show command help",
|
|
8076
|
+
when: "immediate"
|
|
8077
|
+
}
|
|
8078
|
+
};
|
|
8079
|
+
const dynamicRecoveryForCode = (code) => {
|
|
8080
|
+
if (code.startsWith("HTTP_")) return {
|
|
8081
|
+
command: "ogment status",
|
|
8082
|
+
reason: `HTTP error ${code} detected; inspect diagnostics and remote connectivity.`,
|
|
8083
|
+
title: "Inspect diagnostics",
|
|
8084
|
+
when: "immediate"
|
|
8085
|
+
};
|
|
8086
|
+
if (code.startsWith("MCP_")) return {
|
|
8087
|
+
command: "ogment status",
|
|
8088
|
+
reason: `MCP error ${code} detected; inspect diagnostics and remote server state.`,
|
|
8089
|
+
title: "Inspect diagnostics",
|
|
8090
|
+
when: "immediate"
|
|
8091
|
+
};
|
|
8092
|
+
};
|
|
8093
|
+
const hasPlaceholderTokens = (command) => {
|
|
8094
|
+
return command.includes("<") || command.includes(">");
|
|
8095
|
+
};
|
|
8096
|
+
const sanitizedCommand = (command) => {
|
|
8097
|
+
if (typeof command !== "string") return;
|
|
8098
|
+
if (hasPlaceholderTokens(command)) return;
|
|
8099
|
+
return command;
|
|
8100
|
+
};
|
|
8101
|
+
const normalizeRecovery = (code, override) => {
|
|
8102
|
+
const fallback = (isStaticErrorCode(code) ? defaultRecoveryByCode[code] : void 0) ?? dynamicRecoveryForCode(code);
|
|
8103
|
+
if (fallback === void 0) return;
|
|
8104
|
+
const overrideCommand = sanitizedCommand(override?.command);
|
|
8105
|
+
const command = overrideCommand ?? fallback.command;
|
|
8106
|
+
const usesCustomCommand = overrideCommand !== void 0 && overrideCommand !== fallback.command;
|
|
8107
|
+
return {
|
|
8108
|
+
command,
|
|
8109
|
+
id: override?.id ?? `recover_${code.toLowerCase()}`,
|
|
8110
|
+
reason: override?.reason ?? (usesCustomCommand ? `Suggested recovery for error ${code}.` : fallback.reason),
|
|
8111
|
+
title: override?.title ?? (usesCustomCommand ? "Run suggested command" : fallback.title),
|
|
8112
|
+
when: override?.when ?? fallback.when
|
|
8113
|
+
};
|
|
8114
|
+
};
|
|
8115
|
+
|
|
8116
|
+
//#endregion
|
|
8117
|
+
//#region src/shared/errors.ts
|
|
8118
|
+
const ValidationErrorBase = TaggedError("ValidationError")();
|
|
8119
|
+
var ValidationError = class extends ValidationErrorBase {
|
|
8120
|
+
category = ERROR_CATEGORY.validation;
|
|
8121
|
+
code;
|
|
8122
|
+
recovery;
|
|
8123
|
+
retryable = false;
|
|
8124
|
+
constructor(payload) {
|
|
8125
|
+
super({
|
|
8126
|
+
message: payload.message,
|
|
8127
|
+
...payload.details === void 0 ? {} : { details: payload.details }
|
|
8128
|
+
});
|
|
8129
|
+
this.code = payload.code ?? ERROR_CODE.validationInvalidInput;
|
|
8130
|
+
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
8131
|
+
}
|
|
8132
|
+
};
|
|
8133
|
+
const AuthErrorBase = TaggedError("AuthError")();
|
|
8134
|
+
var AuthError = class extends AuthErrorBase {
|
|
8135
|
+
category = ERROR_CATEGORY.auth;
|
|
8136
|
+
code;
|
|
8137
|
+
recovery;
|
|
8138
|
+
retryable;
|
|
8139
|
+
constructor(payload) {
|
|
8140
|
+
super({
|
|
8141
|
+
message: payload.message,
|
|
8142
|
+
...payload.details === void 0 ? {} : { details: payload.details }
|
|
8143
|
+
});
|
|
8144
|
+
this.code = payload.code ?? ERROR_CODE.authRequired;
|
|
8145
|
+
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
8146
|
+
this.retryable = payload.retryable ?? this.code === ERROR_CODE.authDeviceExpired;
|
|
8147
|
+
}
|
|
8148
|
+
};
|
|
8149
|
+
const NotFoundErrorBase = TaggedError("NotFoundError")();
|
|
8150
|
+
var NotFoundError = class extends NotFoundErrorBase {
|
|
8151
|
+
category = ERROR_CATEGORY.notFound;
|
|
8152
|
+
code;
|
|
8153
|
+
recovery;
|
|
8154
|
+
retryable = false;
|
|
8155
|
+
constructor(payload) {
|
|
8156
|
+
super({
|
|
8157
|
+
message: payload.message,
|
|
8158
|
+
resource: payload.resource
|
|
8159
|
+
});
|
|
8160
|
+
this.code = payload.code ?? ERROR_CODE.toolNotFound;
|
|
8161
|
+
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
8162
|
+
}
|
|
8163
|
+
};
|
|
8164
|
+
const RemoteRequestErrorBase = TaggedError("RemoteRequestError")();
|
|
8165
|
+
const parseHttpCode = (code) => {
|
|
8166
|
+
if (!code.startsWith("HTTP_")) return;
|
|
8167
|
+
const parsed = Number.parseInt(code.slice(5), 10);
|
|
8168
|
+
if (!Number.isFinite(parsed)) return;
|
|
8169
|
+
return parsed;
|
|
8170
|
+
};
|
|
8171
|
+
const resolveRemoteErrorCode = (payload, diagnostics) => {
|
|
8172
|
+
if (payload.code !== void 0) return payload.code;
|
|
8173
|
+
if (typeof diagnostics.httpStatus === "number") return `HTTP_${diagnostics.httpStatus}`;
|
|
8174
|
+
if (typeof diagnostics.mcpCode === "number") return `MCP_${diagnostics.mcpCode}`;
|
|
8175
|
+
return ERROR_CODE.transportRequestFailed;
|
|
8176
|
+
};
|
|
8177
|
+
const resolveRemoteDiagnostics = (payload) => {
|
|
8178
|
+
const source = payload.source ?? payload.diagnostics?.source ?? "network";
|
|
8179
|
+
const httpStatus = payload.httpStatus ?? payload.status ?? payload.diagnostics?.httpStatus;
|
|
8180
|
+
const mcpCode = payload.mcpCode ?? payload.diagnostics?.mcpCode;
|
|
8181
|
+
const mcpData = payload.mcpData ?? payload.diagnostics?.mcpData;
|
|
8182
|
+
const operation = payload.operation ?? payload.diagnostics?.operation;
|
|
8183
|
+
const raw = payload.raw ?? payload.diagnostics?.raw;
|
|
8184
|
+
return {
|
|
8185
|
+
source,
|
|
8186
|
+
...httpStatus === void 0 ? {} : { httpStatus },
|
|
8187
|
+
...mcpCode === void 0 ? {} : { mcpCode },
|
|
8188
|
+
...mcpData === void 0 ? {} : { mcpData },
|
|
8189
|
+
...operation === void 0 ? {} : { operation },
|
|
8190
|
+
...raw === void 0 ? {} : { raw }
|
|
8191
|
+
};
|
|
8192
|
+
};
|
|
8193
|
+
const resolveRetryableRemoteError = (diagnostics, code) => {
|
|
8194
|
+
const httpStatus = diagnostics.httpStatus ?? parseHttpCode(code);
|
|
8195
|
+
if (typeof httpStatus === "number") return httpStatus === 429 || httpStatus >= 500;
|
|
8196
|
+
if (typeof diagnostics.mcpCode === "number") return diagnostics.mcpCode === -32001;
|
|
8197
|
+
return diagnostics.source === "network";
|
|
8198
|
+
};
|
|
8199
|
+
const resolveRemoteCategory = (code, diagnostics) => {
|
|
8200
|
+
const httpStatus = diagnostics.httpStatus ?? parseHttpCode(code);
|
|
8201
|
+
if (code === ERROR_CODE.remoteRateLimited || httpStatus === 429) return ERROR_CATEGORY.rateLimit;
|
|
8202
|
+
return ERROR_CATEGORY.remote;
|
|
8203
|
+
};
|
|
8204
|
+
var RemoteRequestError = class extends RemoteRequestErrorBase {
|
|
8205
|
+
category;
|
|
8206
|
+
code;
|
|
8207
|
+
diagnostics;
|
|
8208
|
+
httpStatus;
|
|
8209
|
+
recovery;
|
|
8210
|
+
retryable;
|
|
8211
|
+
constructor(payload) {
|
|
8212
|
+
const diagnostics = resolveRemoteDiagnostics(payload);
|
|
8213
|
+
const code = resolveRemoteErrorCode(payload, diagnostics);
|
|
8214
|
+
const status = diagnostics.httpStatus;
|
|
8215
|
+
super({
|
|
8216
|
+
message: payload.message,
|
|
8217
|
+
...payload.body === void 0 ? {} : { body: payload.body },
|
|
8218
|
+
...status === void 0 ? {} : { status },
|
|
8219
|
+
source: diagnostics.source,
|
|
8220
|
+
...diagnostics.mcpCode === void 0 ? {} : { mcpCode: diagnostics.mcpCode },
|
|
8221
|
+
...diagnostics.mcpData === void 0 ? {} : { mcpData: diagnostics.mcpData },
|
|
8222
|
+
...diagnostics.operation === void 0 ? {} : { operation: diagnostics.operation },
|
|
8223
|
+
...diagnostics.raw === void 0 ? {} : { raw: diagnostics.raw }
|
|
8224
|
+
});
|
|
8225
|
+
this.code = code;
|
|
8226
|
+
this.category = resolveRemoteCategory(code, diagnostics);
|
|
8227
|
+
this.diagnostics = diagnostics;
|
|
8228
|
+
this.httpStatus = diagnostics.httpStatus;
|
|
8229
|
+
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
8230
|
+
this.retryable = payload.retryable ?? resolveRetryableRemoteError(diagnostics, code);
|
|
8231
|
+
}
|
|
8232
|
+
};
|
|
8233
|
+
const ContractMismatchErrorBase = TaggedError("ContractMismatchError")();
|
|
8234
|
+
var ContractMismatchError = class extends ContractMismatchErrorBase {
|
|
8235
|
+
category = ERROR_CATEGORY.contractMismatch;
|
|
8236
|
+
code;
|
|
8237
|
+
recovery;
|
|
8238
|
+
retryable = false;
|
|
8239
|
+
constructor(payload) {
|
|
8240
|
+
super({
|
|
8241
|
+
message: payload.message,
|
|
8242
|
+
...payload.details === void 0 ? {} : { details: payload.details }
|
|
8243
|
+
});
|
|
8244
|
+
this.code = payload.code ?? ERROR_CODE.contractVersionUnsupported;
|
|
8245
|
+
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
8246
|
+
}
|
|
8247
|
+
};
|
|
8248
|
+
const UpdateRequiredErrorBase = TaggedError("UpdateRequiredError")();
|
|
8249
|
+
var UpdateRequiredError = class extends UpdateRequiredErrorBase {
|
|
8250
|
+
category = ERROR_CATEGORY.updateRequired;
|
|
8251
|
+
code;
|
|
8252
|
+
recovery;
|
|
8253
|
+
retryable = false;
|
|
8254
|
+
constructor(payload) {
|
|
8255
|
+
super({
|
|
8256
|
+
currentVersion: payload.currentVersion,
|
|
8257
|
+
latestVersion: payload.latestVersion,
|
|
8258
|
+
message: payload.message,
|
|
8259
|
+
packageName: payload.packageName
|
|
8260
|
+
});
|
|
8261
|
+
this.code = ERROR_CODE.cliUpdateRequired;
|
|
8262
|
+
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
8263
|
+
}
|
|
8264
|
+
};
|
|
8265
|
+
const UnexpectedErrorBase = TaggedError("UnexpectedError")();
|
|
8266
|
+
var UnexpectedError = class extends UnexpectedErrorBase {
|
|
8267
|
+
category = ERROR_CATEGORY.internal;
|
|
8268
|
+
code;
|
|
8269
|
+
recovery;
|
|
8270
|
+
retryable = false;
|
|
8271
|
+
constructor(payload) {
|
|
8272
|
+
super({
|
|
8273
|
+
message: payload.message,
|
|
8274
|
+
...payload.cause === void 0 ? {} : { cause: payload.cause }
|
|
8275
|
+
});
|
|
8276
|
+
this.code = payload.code ?? ERROR_CODE.internalUnexpected;
|
|
8277
|
+
this.recovery = normalizeRecovery(this.code, payload.recovery);
|
|
8278
|
+
}
|
|
8279
|
+
};
|
|
8280
|
+
const withIfDefined = (base, key, value) => {
|
|
8281
|
+
if (value === void 0) return base;
|
|
8282
|
+
return {
|
|
8283
|
+
...base,
|
|
8284
|
+
[key]: value
|
|
8285
|
+
};
|
|
8286
|
+
};
|
|
8287
|
+
const hasDetails = (error) => {
|
|
8288
|
+
return error._tag === "AuthError" || error._tag === "ValidationError";
|
|
8289
|
+
};
|
|
8290
|
+
const appErrorMeta = (error) => {
|
|
8291
|
+
return {
|
|
8292
|
+
category: error.category,
|
|
8293
|
+
code: error.code,
|
|
8294
|
+
recovery: error.recovery,
|
|
8295
|
+
retryable: error.retryable
|
|
8296
|
+
};
|
|
8297
|
+
};
|
|
8298
|
+
const appErrorDiagnostics = (error) => {
|
|
8299
|
+
if (error._tag === "RemoteRequestError") {
|
|
8300
|
+
let diagnostics = { source: error.source };
|
|
8301
|
+
diagnostics = withIfDefined(diagnostics, "httpStatus", error.httpStatus);
|
|
8302
|
+
diagnostics = withIfDefined(diagnostics, "mcpCode", error.mcpCode);
|
|
8303
|
+
diagnostics = withIfDefined(diagnostics, "mcpData", error.mcpData);
|
|
8304
|
+
diagnostics = withIfDefined(diagnostics, "operation", error.operation);
|
|
8305
|
+
diagnostics = withIfDefined(diagnostics, "raw", error.raw);
|
|
8306
|
+
diagnostics = withIfDefined(diagnostics, "body", error.body);
|
|
8307
|
+
return diagnostics;
|
|
8308
|
+
}
|
|
8309
|
+
if (error._tag === "NotFoundError") return { resource: error.resource };
|
|
8310
|
+
if (error._tag === "UpdateRequiredError") return {
|
|
8311
|
+
currentVersion: error.currentVersion,
|
|
8312
|
+
latestVersion: error.latestVersion,
|
|
8313
|
+
packageName: error.packageName
|
|
8314
|
+
};
|
|
8315
|
+
if (error._tag === "UnexpectedError") {
|
|
8316
|
+
if (error.cause === void 0) return {};
|
|
8317
|
+
return { cause: toJsonValue(error.cause) };
|
|
8318
|
+
}
|
|
8319
|
+
if (hasDetails(error) && typeof error.details === "string" && error.details.length > 0) return { details: error.details };
|
|
8320
|
+
return {};
|
|
8321
|
+
};
|
|
8322
|
+
const appErrorTitle = (error) => {
|
|
8323
|
+
if (error._tag === "UnexpectedError") return "Unexpected CLI error";
|
|
8324
|
+
if (error._tag === "ContractMismatchError") return "Contract version unsupported";
|
|
8325
|
+
if (error._tag === "UpdateRequiredError") return "CLI update required";
|
|
8326
|
+
return error.message;
|
|
8327
|
+
};
|
|
8328
|
+
const formatAppError = (error) => {
|
|
8329
|
+
if (error._tag === "NotFoundError") return `${error.message} (${error.resource})`;
|
|
8330
|
+
if (error._tag === "RemoteRequestError" && typeof error.httpStatus === "number") return `${error.message} (status ${error.httpStatus})`;
|
|
8331
|
+
if (hasDetails(error) && typeof error.details === "string" && error.details.length > 0) return `${error.message}: ${error.details}`;
|
|
8332
|
+
return error.message;
|
|
8333
|
+
};
|
|
8334
|
+
|
|
8335
|
+
//#endregion
|
|
8336
|
+
//#region src/shared/guards.ts
|
|
8337
|
+
const formatIssues = (issues) => {
|
|
8338
|
+
return issues.map((issue) => {
|
|
8339
|
+
if (issue.path.length === 0) return issue.message;
|
|
8340
|
+
return `${issue.path.filter((value) => typeof value === "number" || typeof value === "string").join(".")}: ${issue.message}`;
|
|
8341
|
+
}).join("; ");
|
|
8342
|
+
};
|
|
8343
|
+
const parseWithSchema = (schema, payload, context, options = {}) => {
|
|
8344
|
+
const parsed = schema.safeParse(payload);
|
|
8345
|
+
if (!parsed.success) return Result.err(new ValidationError({
|
|
8346
|
+
details: formatIssues(parsed.error.issues),
|
|
8347
|
+
message: `Invalid ${context}`,
|
|
8348
|
+
...options.code === void 0 ? {} : { code: options.code },
|
|
8349
|
+
...options.recovery === void 0 ? {} : { recovery: options.recovery }
|
|
8350
|
+
}));
|
|
8351
|
+
return Result.ok(parsed.data);
|
|
8352
|
+
};
|
|
8353
|
+
const parseJsonValue = (raw, context) => {
|
|
8354
|
+
return Result.try({
|
|
8355
|
+
catch: () => {
|
|
8356
|
+
return new ValidationError({
|
|
8357
|
+
details: raw,
|
|
8358
|
+
message: `Invalid JSON in ${context}`
|
|
8359
|
+
});
|
|
8360
|
+
},
|
|
8361
|
+
try: () => {
|
|
8362
|
+
return toJsonValue(JSON.parse(raw));
|
|
8363
|
+
}
|
|
8364
|
+
});
|
|
8365
|
+
};
|
|
8366
|
+
const isRecord$2 = (value) => {
|
|
8367
|
+
return typeof value === "object" && value !== null;
|
|
8368
|
+
};
|
|
8369
|
+
|
|
8339
8370
|
//#endregion
|
|
8340
8371
|
//#region src/shared/schemas.ts
|
|
8341
8372
|
const credentialsFileSchema = object({
|
|
@@ -8345,35 +8376,326 @@ const credentialsFileSchema = object({
|
|
|
8345
8376
|
|
|
8346
8377
|
//#endregion
|
|
8347
8378
|
//#region src/infra/credentials.ts
|
|
8348
|
-
const
|
|
8349
|
-
|
|
8379
|
+
const authStateFileSchema = object({
|
|
8380
|
+
activeAuth: object({
|
|
8381
|
+
agentName: string().optional(),
|
|
8382
|
+
apiKey: string().min(1),
|
|
8383
|
+
boundAt: string().optional(),
|
|
8384
|
+
namespaceKey: string().min(1).optional(),
|
|
8385
|
+
scopeFingerprint: string().min(1).optional()
|
|
8386
|
+
}).nullable().optional(),
|
|
8387
|
+
installationId: uuid(),
|
|
8388
|
+
pendingRequests: array(object({
|
|
8389
|
+
authRequestId: string().min(1),
|
|
8390
|
+
code: string().min(1),
|
|
8391
|
+
createdAt: string().min(1),
|
|
8392
|
+
expiresAt: string().min(1),
|
|
8393
|
+
namespaceKey: string().min(1),
|
|
8394
|
+
scopeFingerprint: string().min(1).optional(),
|
|
8395
|
+
verificationUrl: string().min(1)
|
|
8396
|
+
})).optional(),
|
|
8397
|
+
version: literal(1)
|
|
8398
|
+
}).strict();
|
|
8399
|
+
const telemetryInstallationSchema = object({ installationId: uuid() }).strict();
|
|
8400
|
+
const LOCK_RETRY_DELAY_MS = 10;
|
|
8401
|
+
const LOCK_STALE_MS = 3e4;
|
|
8402
|
+
const LOCK_TIMEOUT_MS = 2e3;
|
|
8403
|
+
const sleepBlocking = (milliseconds) => {
|
|
8404
|
+
const buffer = new SharedArrayBuffer(4);
|
|
8405
|
+
const view = new Int32Array(buffer);
|
|
8406
|
+
Atomics.wait(view, 0, 0, milliseconds);
|
|
8350
8407
|
};
|
|
8351
8408
|
const normalizeCredentials = (value) => {
|
|
8352
|
-
const parsed = parseWithSchema(credentialsFileSchema, value, "credentials file");
|
|
8409
|
+
const parsed = parseWithSchema(credentialsFileSchema, value, "legacy credentials file");
|
|
8410
|
+
if (Result.isError(parsed) || parsed.value.apiKey === void 0) return null;
|
|
8411
|
+
return {
|
|
8412
|
+
...parsed.value.agentName === void 0 ? {} : { agentName: parsed.value.agentName },
|
|
8413
|
+
apiKey: parsed.value.apiKey
|
|
8414
|
+
};
|
|
8415
|
+
};
|
|
8416
|
+
const normalizeAuthState = (value) => {
|
|
8417
|
+
const parsed = parseWithSchema(authStateFileSchema, value, "auth state file");
|
|
8353
8418
|
if (Result.isError(parsed)) return null;
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8419
|
+
const activeAuth = parsed.value.activeAuth === void 0 || parsed.value.activeAuth === null ? null : {
|
|
8420
|
+
...parsed.value.activeAuth.agentName === void 0 ? {} : { agentName: parsed.value.activeAuth.agentName },
|
|
8421
|
+
apiKey: parsed.value.activeAuth.apiKey,
|
|
8422
|
+
...parsed.value.activeAuth.boundAt === void 0 ? {} : { boundAt: parsed.value.activeAuth.boundAt },
|
|
8423
|
+
...parsed.value.activeAuth.namespaceKey === void 0 ? {} : { namespaceKey: parsed.value.activeAuth.namespaceKey },
|
|
8424
|
+
...parsed.value.activeAuth.scopeFingerprint === void 0 ? {} : { scopeFingerprint: parsed.value.activeAuth.scopeFingerprint }
|
|
8425
|
+
};
|
|
8426
|
+
const pendingRequests = (parsed.value.pendingRequests ?? []).map((request) => {
|
|
8427
|
+
return {
|
|
8428
|
+
authRequestId: request.authRequestId,
|
|
8429
|
+
code: request.code,
|
|
8430
|
+
createdAt: request.createdAt,
|
|
8431
|
+
expiresAt: request.expiresAt,
|
|
8432
|
+
namespaceKey: request.namespaceKey,
|
|
8433
|
+
...request.scopeFingerprint === void 0 ? {} : { scopeFingerprint: request.scopeFingerprint },
|
|
8434
|
+
verificationUrl: request.verificationUrl
|
|
8435
|
+
};
|
|
8436
|
+
});
|
|
8437
|
+
return {
|
|
8438
|
+
activeAuth,
|
|
8439
|
+
installationId: parsed.value.installationId,
|
|
8440
|
+
pendingRequests,
|
|
8441
|
+
version: 1
|
|
8442
|
+
};
|
|
8443
|
+
};
|
|
8444
|
+
const toCorruptPath = (path, nowFn) => {
|
|
8445
|
+
return `${path}.corrupt-${nowFn().toISOString().replaceAll(":", "-")}`;
|
|
8446
|
+
};
|
|
8447
|
+
const isErrnoException = (error) => {
|
|
8448
|
+
if (!(error instanceof Error) || !("code" in error)) return false;
|
|
8449
|
+
const code = Reflect.get(error, "code");
|
|
8450
|
+
return code === void 0 || typeof code === "string";
|
|
8451
|
+
};
|
|
8452
|
+
const rethrowWithCleanupError = (operationError, cleanupError) => {
|
|
8453
|
+
if (operationError instanceof Error) {
|
|
8454
|
+
if (operationError.cause === void 0) Reflect.set(operationError, "cause", cleanupError);
|
|
8455
|
+
else Reflect.set(operationError, "cleanupError", cleanupError);
|
|
8456
|
+
throw operationError;
|
|
8457
|
+
}
|
|
8458
|
+
const wrappedError = new Error("Auth-state operation failed and lock cleanup also failed", { cause: operationError });
|
|
8459
|
+
Reflect.set(wrappedError, "cleanupError", cleanupError);
|
|
8460
|
+
throw wrappedError;
|
|
8360
8461
|
};
|
|
8361
8462
|
const createFileCredentialsStore = (deps) => {
|
|
8362
8463
|
const existsSyncFn = deps.existsSyncFn ?? existsSync;
|
|
8464
|
+
const fsyncSyncFn = deps.fsyncSyncFn ?? fsyncSync;
|
|
8363
8465
|
const lstatSyncFn = deps.lstatSyncFn ?? lstatSync;
|
|
8364
8466
|
const mkdirSyncFn = deps.mkdirSyncFn ?? mkdirSync;
|
|
8467
|
+
const nowFn = deps.nowFn ?? (() => /* @__PURE__ */ new Date());
|
|
8468
|
+
const openSyncFn = deps.openSyncFn ?? openSync;
|
|
8469
|
+
const randomUuidFn = deps.randomUuidFn ?? randomUUID;
|
|
8365
8470
|
const readFileSyncFn = deps.readFileSyncFn ?? readFileSync;
|
|
8471
|
+
const renameSyncFn = deps.renameSyncFn ?? renameSync;
|
|
8472
|
+
const statSyncFn = deps.statSyncFn ?? statSync;
|
|
8366
8473
|
const unlinkSyncFn = deps.unlinkSyncFn ?? unlinkSync;
|
|
8367
8474
|
const writeFileSyncFn = deps.writeFileSyncFn ?? writeFileSync;
|
|
8475
|
+
const lockPath = `${deps.credentialsPath}.lock`;
|
|
8476
|
+
const readJsonFile = (path) => {
|
|
8477
|
+
if (!existsSyncFn(path)) return null;
|
|
8478
|
+
try {
|
|
8479
|
+
return JSON.parse(readFileSyncFn(path, "utf8"));
|
|
8480
|
+
} catch {
|
|
8481
|
+
return null;
|
|
8482
|
+
}
|
|
8483
|
+
};
|
|
8484
|
+
const readLegacyCredentials = () => {
|
|
8485
|
+
return normalizeCredentials(readJsonFile(deps.legacyCredentialsPath));
|
|
8486
|
+
};
|
|
8487
|
+
const readLegacyInstallationId = () => {
|
|
8488
|
+
const raw = readJsonFile(deps.telemetryPath);
|
|
8489
|
+
if (raw === null) return null;
|
|
8490
|
+
const parsed = telemetryInstallationSchema.safeParse(raw);
|
|
8491
|
+
return parsed.success ? parsed.data.installationId : null;
|
|
8492
|
+
};
|
|
8493
|
+
const createEmptyState = (installationId = randomUuidFn()) => {
|
|
8494
|
+
return {
|
|
8495
|
+
activeAuth: null,
|
|
8496
|
+
installationId,
|
|
8497
|
+
pendingRequests: [],
|
|
8498
|
+
version: 1
|
|
8499
|
+
};
|
|
8500
|
+
};
|
|
8501
|
+
const withLock = (operation) => {
|
|
8502
|
+
mkdirSyncFn(deps.configDir, { recursive: true });
|
|
8503
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
8504
|
+
let lockFd = null;
|
|
8505
|
+
const releaseLock = (fd) => {
|
|
8506
|
+
closeSync(fd);
|
|
8507
|
+
try {
|
|
8508
|
+
unlinkSyncFn(lockPath);
|
|
8509
|
+
} catch (releaseError) {
|
|
8510
|
+
if (!isErrnoException(releaseError) || releaseError.code !== "ENOENT") throw releaseError;
|
|
8511
|
+
}
|
|
8512
|
+
};
|
|
8513
|
+
while (lockFd === null) try {
|
|
8514
|
+
lockFd = openSyncFn(lockPath, "wx", 384);
|
|
8515
|
+
} catch (error) {
|
|
8516
|
+
if (!isErrnoException(error) || error.code !== "EEXIST") throw error;
|
|
8517
|
+
try {
|
|
8518
|
+
if (Date.now() - statSyncFn(lockPath).mtimeMs >= LOCK_STALE_MS) {
|
|
8519
|
+
unlinkSyncFn(lockPath);
|
|
8520
|
+
continue;
|
|
8521
|
+
}
|
|
8522
|
+
} catch (statError) {
|
|
8523
|
+
if (!isErrnoException(statError) || statError.code !== "ENOENT") throw statError;
|
|
8524
|
+
}
|
|
8525
|
+
if (Date.now() >= deadline) throw new Error("Timed out waiting for auth-state lock", { cause: error });
|
|
8526
|
+
sleepBlocking(LOCK_RETRY_DELAY_MS);
|
|
8527
|
+
}
|
|
8528
|
+
if (lockFd === null) throw new Error("Failed to acquire auth-state lock");
|
|
8529
|
+
const acquiredLockFd = lockFd;
|
|
8530
|
+
try {
|
|
8531
|
+
const result = operation();
|
|
8532
|
+
releaseLock(acquiredLockFd);
|
|
8533
|
+
return result;
|
|
8534
|
+
} catch (operationError) {
|
|
8535
|
+
let cleanupError;
|
|
8536
|
+
try {
|
|
8537
|
+
releaseLock(acquiredLockFd);
|
|
8538
|
+
} catch (releaseError) {
|
|
8539
|
+
cleanupError = releaseError;
|
|
8540
|
+
}
|
|
8541
|
+
if (cleanupError !== void 0) rethrowWithCleanupError(operationError, cleanupError);
|
|
8542
|
+
throw operationError;
|
|
8543
|
+
}
|
|
8544
|
+
};
|
|
8545
|
+
const writeState = (state) => {
|
|
8546
|
+
mkdirSyncFn(deps.configDir, { recursive: true });
|
|
8547
|
+
if (existsSyncFn(deps.credentialsPath) && lstatSyncFn(deps.credentialsPath).isSymbolicLink()) unlinkSyncFn(deps.credentialsPath);
|
|
8548
|
+
const tempPath = `${deps.credentialsPath}.tmp-${process.pid}-${randomUuidFn()}`;
|
|
8549
|
+
let tempFd = null;
|
|
8550
|
+
try {
|
|
8551
|
+
tempFd = openSyncFn(tempPath, "w", 384);
|
|
8552
|
+
writeFileSyncFn(tempFd, `${JSON.stringify(state, null, 2)}\n`, "utf8");
|
|
8553
|
+
fsyncSyncFn(tempFd);
|
|
8554
|
+
closeSync(tempFd);
|
|
8555
|
+
tempFd = null;
|
|
8556
|
+
renameSyncFn(tempPath, deps.credentialsPath);
|
|
8557
|
+
const configDirFd = openSyncFn(dirname(deps.credentialsPath), "r");
|
|
8558
|
+
try {
|
|
8559
|
+
fsyncSyncFn(configDirFd);
|
|
8560
|
+
} finally {
|
|
8561
|
+
closeSync(configDirFd);
|
|
8562
|
+
}
|
|
8563
|
+
} catch (error) {
|
|
8564
|
+
if (tempFd !== null) closeSync(tempFd);
|
|
8565
|
+
try {
|
|
8566
|
+
unlinkSyncFn(tempPath);
|
|
8567
|
+
} catch (unlinkError) {
|
|
8568
|
+
if (!isErrnoException(unlinkError) || unlinkError.code !== "ENOENT") throw unlinkError;
|
|
8569
|
+
}
|
|
8570
|
+
throw error;
|
|
8571
|
+
}
|
|
8572
|
+
};
|
|
8573
|
+
const quarantineCorruptState = () => {
|
|
8574
|
+
if (!existsSyncFn(deps.credentialsPath)) return;
|
|
8575
|
+
try {
|
|
8576
|
+
renameSyncFn(deps.credentialsPath, toCorruptPath(deps.credentialsPath, nowFn));
|
|
8577
|
+
} catch {}
|
|
8578
|
+
};
|
|
8579
|
+
const loadStateFromDisk = () => {
|
|
8580
|
+
if (!existsSyncFn(deps.credentialsPath)) return null;
|
|
8581
|
+
let parsedJson;
|
|
8582
|
+
try {
|
|
8583
|
+
parsedJson = JSON.parse(readFileSyncFn(deps.credentialsPath, "utf8"));
|
|
8584
|
+
} catch (error) {
|
|
8585
|
+
if (!isErrnoException(error) || error.code === void 0) {
|
|
8586
|
+
quarantineCorruptState();
|
|
8587
|
+
return null;
|
|
8588
|
+
}
|
|
8589
|
+
throw error;
|
|
8590
|
+
}
|
|
8591
|
+
const parsed = normalizeAuthState(parsedJson);
|
|
8592
|
+
if (parsed === null) {
|
|
8593
|
+
quarantineCorruptState();
|
|
8594
|
+
return null;
|
|
8595
|
+
}
|
|
8596
|
+
return parsed;
|
|
8597
|
+
};
|
|
8598
|
+
const migrateStateFromLegacy = () => {
|
|
8599
|
+
const migratedInstallationId = readLegacyInstallationId();
|
|
8600
|
+
const migratedCredentials = readLegacyCredentials();
|
|
8601
|
+
if (!(migratedInstallationId !== null || migratedCredentials !== null)) return null;
|
|
8602
|
+
return {
|
|
8603
|
+
...createEmptyState(migratedInstallationId ?? randomUuidFn()),
|
|
8604
|
+
...migratedCredentials === null ? {} : { activeAuth: migratedCredentials }
|
|
8605
|
+
};
|
|
8606
|
+
};
|
|
8607
|
+
const loadState = (options) => {
|
|
8608
|
+
const currentState = loadStateFromDisk();
|
|
8609
|
+
if (currentState !== null) return currentState;
|
|
8610
|
+
const migratedState = migrateStateFromLegacy();
|
|
8611
|
+
if (migratedState !== null) {
|
|
8612
|
+
withLock(() => {
|
|
8613
|
+
if (!existsSyncFn(deps.credentialsPath)) writeState(migratedState);
|
|
8614
|
+
});
|
|
8615
|
+
return loadStateFromDisk() ?? migratedState;
|
|
8616
|
+
}
|
|
8617
|
+
if (options?.persistIfMissing === true) {
|
|
8618
|
+
const emptyState = createEmptyState();
|
|
8619
|
+
withLock(() => {
|
|
8620
|
+
if (!existsSyncFn(deps.credentialsPath)) writeState(emptyState);
|
|
8621
|
+
});
|
|
8622
|
+
return loadStateFromDisk() ?? emptyState;
|
|
8623
|
+
}
|
|
8624
|
+
return null;
|
|
8625
|
+
};
|
|
8368
8626
|
return {
|
|
8627
|
+
clearPendingRequests: (namespaceKey) => {
|
|
8628
|
+
return Result.try({
|
|
8629
|
+
catch: (cause) => new UnexpectedError({
|
|
8630
|
+
cause,
|
|
8631
|
+
message: "Failed to update local auth state"
|
|
8632
|
+
}),
|
|
8633
|
+
try: () => {
|
|
8634
|
+
withLock(() => {
|
|
8635
|
+
const latest = loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState();
|
|
8636
|
+
writeState({
|
|
8637
|
+
...latest,
|
|
8638
|
+
pendingRequests: namespaceKey === void 0 ? [] : latest.pendingRequests.filter((request) => request.namespaceKey !== namespaceKey)
|
|
8639
|
+
});
|
|
8640
|
+
});
|
|
8641
|
+
}
|
|
8642
|
+
});
|
|
8643
|
+
},
|
|
8369
8644
|
delete: () => {
|
|
8370
8645
|
return Result.try({
|
|
8371
8646
|
catch: (cause) => new UnexpectedError({
|
|
8372
8647
|
cause,
|
|
8373
|
-
message: "Failed to
|
|
8648
|
+
message: "Failed to update local auth state"
|
|
8649
|
+
}),
|
|
8650
|
+
try: () => {
|
|
8651
|
+
withLock(() => {
|
|
8652
|
+
writeState({
|
|
8653
|
+
...loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState(),
|
|
8654
|
+
activeAuth: null,
|
|
8655
|
+
pendingRequests: []
|
|
8656
|
+
});
|
|
8657
|
+
});
|
|
8658
|
+
}
|
|
8659
|
+
});
|
|
8660
|
+
},
|
|
8661
|
+
deletePendingRequest: (authRequestId) => {
|
|
8662
|
+
return Result.try({
|
|
8663
|
+
catch: (cause) => new UnexpectedError({
|
|
8664
|
+
cause,
|
|
8665
|
+
message: "Failed to update local auth state"
|
|
8666
|
+
}),
|
|
8667
|
+
try: () => {
|
|
8668
|
+
withLock(() => {
|
|
8669
|
+
const latest = loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState();
|
|
8670
|
+
writeState({
|
|
8671
|
+
...latest,
|
|
8672
|
+
pendingRequests: latest.pendingRequests.filter((request) => request.authRequestId !== authRequestId)
|
|
8673
|
+
});
|
|
8674
|
+
});
|
|
8675
|
+
}
|
|
8676
|
+
});
|
|
8677
|
+
},
|
|
8678
|
+
getInstallationId: () => {
|
|
8679
|
+
return Result.try({
|
|
8680
|
+
catch: (cause) => new UnexpectedError({
|
|
8681
|
+
cause,
|
|
8682
|
+
message: "Failed to load local auth state"
|
|
8374
8683
|
}),
|
|
8375
8684
|
try: () => {
|
|
8376
|
-
|
|
8685
|
+
const state = loadState({ persistIfMissing: true });
|
|
8686
|
+
if (state === null) throw new Error("Failed to initialize local auth state");
|
|
8687
|
+
return state.installationId;
|
|
8688
|
+
}
|
|
8689
|
+
});
|
|
8690
|
+
},
|
|
8691
|
+
listPendingRequests: () => {
|
|
8692
|
+
return Result.try({
|
|
8693
|
+
catch: (cause) => new UnexpectedError({
|
|
8694
|
+
cause,
|
|
8695
|
+
message: "Failed to load local auth state"
|
|
8696
|
+
}),
|
|
8697
|
+
try: () => {
|
|
8698
|
+
return loadState()?.pendingRequests ?? [];
|
|
8377
8699
|
}
|
|
8378
8700
|
});
|
|
8379
8701
|
},
|
|
@@ -8381,14 +8703,15 @@ const createFileCredentialsStore = (deps) => {
|
|
|
8381
8703
|
return Result.try({
|
|
8382
8704
|
catch: (cause) => new UnexpectedError({
|
|
8383
8705
|
cause,
|
|
8384
|
-
message: "Failed to load
|
|
8706
|
+
message: "Failed to load local auth state"
|
|
8385
8707
|
}),
|
|
8386
8708
|
try: () => {
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8709
|
+
const state = loadState();
|
|
8710
|
+
if (state?.activeAuth === null || state === null) return null;
|
|
8711
|
+
return {
|
|
8712
|
+
...state.activeAuth.agentName === void 0 ? {} : { agentName: state.activeAuth.agentName },
|
|
8713
|
+
apiKey: state.activeAuth.apiKey
|
|
8714
|
+
};
|
|
8392
8715
|
}
|
|
8393
8716
|
});
|
|
8394
8717
|
},
|
|
@@ -8396,12 +8719,68 @@ const createFileCredentialsStore = (deps) => {
|
|
|
8396
8719
|
return Result.try({
|
|
8397
8720
|
catch: (cause) => new UnexpectedError({
|
|
8398
8721
|
cause,
|
|
8399
|
-
message: "Failed to
|
|
8722
|
+
message: "Failed to update local auth state"
|
|
8400
8723
|
}),
|
|
8401
8724
|
try: () => {
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8725
|
+
withLock(() => {
|
|
8726
|
+
writeState({
|
|
8727
|
+
...loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState(),
|
|
8728
|
+
activeAuth: {
|
|
8729
|
+
...credentials.agentName === void 0 ? {} : { agentName: credentials.agentName },
|
|
8730
|
+
apiKey: credentials.apiKey
|
|
8731
|
+
}
|
|
8732
|
+
});
|
|
8733
|
+
});
|
|
8734
|
+
}
|
|
8735
|
+
});
|
|
8736
|
+
},
|
|
8737
|
+
storeApprovedAuth: (credentials, namespaceKey) => {
|
|
8738
|
+
return Result.try({
|
|
8739
|
+
catch: (cause) => new UnexpectedError({
|
|
8740
|
+
cause,
|
|
8741
|
+
message: "Failed to update local auth state"
|
|
8742
|
+
}),
|
|
8743
|
+
try: () => {
|
|
8744
|
+
withLock(() => {
|
|
8745
|
+
const currentState = loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState();
|
|
8746
|
+
writeState({
|
|
8747
|
+
...currentState,
|
|
8748
|
+
activeAuth: {
|
|
8749
|
+
...credentials.agentName === void 0 ? {} : { agentName: credentials.agentName },
|
|
8750
|
+
apiKey: credentials.apiKey,
|
|
8751
|
+
boundAt: nowFn().toISOString(),
|
|
8752
|
+
...namespaceKey === void 0 ? {} : { namespaceKey }
|
|
8753
|
+
},
|
|
8754
|
+
pendingRequests: namespaceKey === void 0 ? [] : currentState.pendingRequests.filter((request) => request.namespaceKey !== namespaceKey)
|
|
8755
|
+
});
|
|
8756
|
+
});
|
|
8757
|
+
}
|
|
8758
|
+
});
|
|
8759
|
+
},
|
|
8760
|
+
savePendingRequest: (request) => {
|
|
8761
|
+
return Result.try({
|
|
8762
|
+
catch: (cause) => new UnexpectedError({
|
|
8763
|
+
cause,
|
|
8764
|
+
message: "Failed to update local auth state"
|
|
8765
|
+
}),
|
|
8766
|
+
try: () => {
|
|
8767
|
+
withLock(() => {
|
|
8768
|
+
const currentState = loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState();
|
|
8769
|
+
const pendingRequests = currentState.pendingRequests.filter((entry) => entry.authRequestId !== request.authRequestId);
|
|
8770
|
+
pendingRequests.push({
|
|
8771
|
+
authRequestId: request.authRequestId,
|
|
8772
|
+
code: request.code,
|
|
8773
|
+
createdAt: request.createdAt,
|
|
8774
|
+
expiresAt: request.expiresAt,
|
|
8775
|
+
namespaceKey: request.namespaceKey,
|
|
8776
|
+
...request.scopeFingerprint === void 0 ? {} : { scopeFingerprint: request.scopeFingerprint },
|
|
8777
|
+
verificationUrl: request.verificationUrl
|
|
8778
|
+
});
|
|
8779
|
+
writeState({
|
|
8780
|
+
...currentState,
|
|
8781
|
+
pendingRequests
|
|
8782
|
+
});
|
|
8783
|
+
});
|
|
8405
8784
|
}
|
|
8406
8785
|
});
|
|
8407
8786
|
}
|
|
@@ -8421,7 +8800,7 @@ const DEFAULT_OGMENT_BASE_URL = "https://dashboard.ogment.ai";
|
|
|
8421
8800
|
*/
|
|
8422
8801
|
const SENTRY_DSN_BUILD = "https://4219ab98670b61086758b4d0e31ae318@o4507724844957696.ingest.us.sentry.io/4510992932405248";
|
|
8423
8802
|
const SENTRY_ENVIRONMENT_BUILD = "production";
|
|
8424
|
-
const SENTRY_RELEASE_BUILD = "cli-v0.
|
|
8803
|
+
const SENTRY_RELEASE_BUILD = "cli-v0.9.0+git:f88c23e58";
|
|
8425
8804
|
const packageJsonSchema = object({ version: string().min(1) });
|
|
8426
8805
|
const hasCode = (value, code) => {
|
|
8427
8806
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -8477,9 +8856,10 @@ const resolveSentryConfig = (env) => {
|
|
|
8477
8856
|
const telemetryRuntimeFields = (env, configDir) => {
|
|
8478
8857
|
return {
|
|
8479
8858
|
configDir,
|
|
8480
|
-
credentialsPath: join(configDir, "
|
|
8859
|
+
credentialsPath: join(configDir, "auth-state.json"),
|
|
8481
8860
|
envApiKey: env["OGMENT_API_KEY"],
|
|
8482
8861
|
executionEnvironment: detectExecutionEnvironment(env),
|
|
8862
|
+
legacyCredentialsPath: join(configDir, "credentials.json"),
|
|
8483
8863
|
telemetryDisabled: telemetryDisabledFromEnv(env),
|
|
8484
8864
|
telemetryPath: join(configDir, "telemetry.json"),
|
|
8485
8865
|
version: VERSION$2
|
|
@@ -13351,6 +13731,8 @@ const UPDATE_CHECK_CACHE_FILENAME = "update-check.json";
|
|
|
13351
13731
|
const UPDATE_CHECK_INTERVAL_MS = 3600 * 1e3;
|
|
13352
13732
|
const LOOKUP_TIMEOUT_MS = 1500;
|
|
13353
13733
|
const PACKAGE_VERSION_SELECTOR = "latest";
|
|
13734
|
+
const OGMENT_NO_UPDATE_CHECK_ENV_VAR = "OGMENT_NO_UPDATE_CHECK";
|
|
13735
|
+
const NO_UPDATE_NOTIFIER_ENV_VAR = "NO_UPDATE_NOTIFIER";
|
|
13354
13736
|
const cacheSchema = object({
|
|
13355
13737
|
lastCheckedAtMs: number$1().int().nonnegative(),
|
|
13356
13738
|
latestVersion: string().optional()
|
|
@@ -13361,8 +13743,8 @@ const isTruthyEnvValue = (value) => {
|
|
|
13361
13743
|
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
13362
13744
|
};
|
|
13363
13745
|
const shouldSkipUpdateCheck = (env) => {
|
|
13364
|
-
if (isTruthyEnvValue(env[
|
|
13365
|
-
if (isTruthyEnvValue(env[
|
|
13746
|
+
if (isTruthyEnvValue(env[OGMENT_NO_UPDATE_CHECK_ENV_VAR])) return true;
|
|
13747
|
+
if (isTruthyEnvValue(env[NO_UPDATE_NOTIFIER_ENV_VAR])) return true;
|
|
13366
13748
|
if (isTruthyEnvValue(env["CI"])) return true;
|
|
13367
13749
|
return false;
|
|
13368
13750
|
};
|
|
@@ -13406,33 +13788,17 @@ const isNewerVersion = (latestVersion, currentVersion) => {
|
|
|
13406
13788
|
if (latest === null || current === null) return false;
|
|
13407
13789
|
return import_semver.default.gt(latest, current);
|
|
13408
13790
|
};
|
|
13409
|
-
const toUpdateMessage = (currentVersion, latestVersion, packageName) => {
|
|
13410
|
-
return [
|
|
13411
|
-
"",
|
|
13412
|
-
"A newer Ogment CLI is available.",
|
|
13413
|
-
"",
|
|
13414
|
-
`Current: v${currentVersion}`,
|
|
13415
|
-
`Latest: v${latestVersion}`,
|
|
13416
|
-
"",
|
|
13417
|
-
"Recommended update:",
|
|
13418
|
-
`npm i -g ${packageName}`,
|
|
13419
|
-
"",
|
|
13420
|
-
"Continuing with your current version for this run.",
|
|
13421
|
-
""
|
|
13422
|
-
].join("\n");
|
|
13423
|
-
};
|
|
13424
13791
|
const defaultConfigDir = () => {
|
|
13425
13792
|
return join(homedir(), ".config", "ogment");
|
|
13426
13793
|
};
|
|
13427
13794
|
const cachePathFromConfigDir = (configDir) => {
|
|
13428
13795
|
return join(configDir, UPDATE_CHECK_CACHE_FILENAME);
|
|
13429
13796
|
};
|
|
13430
|
-
const
|
|
13797
|
+
const maybeGetAvailableCliUpdate = async (options, deps = {}) => {
|
|
13431
13798
|
try {
|
|
13432
|
-
if (shouldSkipUpdateCheck(options.env ?? process.env)) return;
|
|
13799
|
+
if (shouldSkipUpdateCheck(options.env ?? process.env)) return null;
|
|
13433
13800
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
13434
13801
|
const fetchLatestVersion = deps.fetchLatestVersion ?? defaultFetchLatestVersion;
|
|
13435
|
-
const stderr = options.stderr ?? process.stderr;
|
|
13436
13802
|
const cachePath = cachePathFromConfigDir(options.configDir ?? defaultConfigDir());
|
|
13437
13803
|
let latestVersion;
|
|
13438
13804
|
const existingCache = readCache(cachePath);
|
|
@@ -13448,8 +13814,13 @@ const maybeNotifyCliUpdate = async (options, deps = {}) => {
|
|
|
13448
13814
|
lastCheckedAtMs: nowMs
|
|
13449
13815
|
});
|
|
13450
13816
|
} else latestVersion = existingCache.latestVersion;
|
|
13451
|
-
if (latestVersion !== void 0 && isNewerVersion(latestVersion, options.currentVersion))
|
|
13817
|
+
if (latestVersion !== void 0 && isNewerVersion(latestVersion, options.currentVersion)) return {
|
|
13818
|
+
currentVersion: options.currentVersion,
|
|
13819
|
+
latestVersion,
|
|
13820
|
+
packageName: options.packageName
|
|
13821
|
+
};
|
|
13452
13822
|
} catch {}
|
|
13823
|
+
return null;
|
|
13453
13824
|
};
|
|
13454
13825
|
|
|
13455
13826
|
//#endregion
|
|
@@ -13789,6 +14160,13 @@ const deviceCodeStartDataSchema = object({
|
|
|
13789
14160
|
verificationUri: string().url()
|
|
13790
14161
|
}).strict();
|
|
13791
14162
|
const deviceCodeStartSchema = cliSuccessEnvelopeSchema(deviceCodeStartDataSchema);
|
|
14163
|
+
const authStartDataSchema = object({
|
|
14164
|
+
authRequestId: string().min(1),
|
|
14165
|
+
expiresAt: string().datetime(),
|
|
14166
|
+
userCode: string().min(1),
|
|
14167
|
+
verificationUrl: string().url()
|
|
14168
|
+
}).strict();
|
|
14169
|
+
const authStartSchema = cliSuccessEnvelopeSchema(authStartDataSchema);
|
|
13792
14170
|
const deviceTokenPendingDataSchema = object({ status: literal("authorization_pending") }).strict();
|
|
13793
14171
|
const deviceTokenApprovedDataSchema = object({
|
|
13794
14172
|
agentName: string().min(1),
|
|
@@ -13798,6 +14176,27 @@ const deviceTokenApprovedDataSchema = object({
|
|
|
13798
14176
|
const deviceTokenDataSchema = discriminatedUnion("status", [deviceTokenPendingDataSchema, deviceTokenApprovedDataSchema]);
|
|
13799
14177
|
const deviceTokenSchema = cliSuccessEnvelopeSchema(deviceTokenDataSchema);
|
|
13800
14178
|
const deviceTokenApprovedSchema = cliSuccessEnvelopeSchema(deviceTokenApprovedDataSchema);
|
|
14179
|
+
const authExchangePendingDataSchema = object({ status: literal("authorization_pending") }).strict();
|
|
14180
|
+
const authExchangeApprovedDataSchema = object({
|
|
14181
|
+
agentName: string().min(1),
|
|
14182
|
+
apiKey: string().min(1),
|
|
14183
|
+
scopeFingerprint: string().min(1),
|
|
14184
|
+
status: literal("approved")
|
|
14185
|
+
}).strict();
|
|
14186
|
+
const authExchangeExpiredDataSchema = object({ status: literal("expired") }).strict();
|
|
14187
|
+
const authExchangeDeniedDataSchema = object({ status: literal("denied") }).strict();
|
|
14188
|
+
const authExchangeConflictDataSchema = object({ status: literal("conflict") }).strict();
|
|
14189
|
+
const authExchangeInvalidDataSchema = object({ status: literal("invalid") }).strict();
|
|
14190
|
+
const authExchangeDataSchema = discriminatedUnion("status", [
|
|
14191
|
+
authExchangePendingDataSchema,
|
|
14192
|
+
authExchangeApprovedDataSchema,
|
|
14193
|
+
authExchangeExpiredDataSchema,
|
|
14194
|
+
authExchangeDeniedDataSchema,
|
|
14195
|
+
authExchangeConflictDataSchema,
|
|
14196
|
+
authExchangeInvalidDataSchema
|
|
14197
|
+
]);
|
|
14198
|
+
const authExchangeSchema = cliSuccessEnvelopeSchema(authExchangeDataSchema);
|
|
14199
|
+
const authExchangeApprovedSchema = cliSuccessEnvelopeSchema(authExchangeApprovedDataSchema);
|
|
13801
14200
|
const revokeSelfDataSchema = object({ success: literal(true) }).strict();
|
|
13802
14201
|
const revokeSelfSchema = cliSuccessEnvelopeSchema(revokeSelfDataSchema);
|
|
13803
14202
|
|
|
@@ -13805,6 +14204,8 @@ const revokeSelfSchema = cliSuccessEnvelopeSchema(revokeSelfDataSchema);
|
|
|
13805
14204
|
//#region ../../packages/shared/src/cli/endpoints.ts
|
|
13806
14205
|
const cliEndpoints = {
|
|
13807
14206
|
accountMe: "/api/v1/cli/me",
|
|
14207
|
+
authExchange: "/api/v1/cli/auth/exchange",
|
|
14208
|
+
authStart: "/api/v1/cli/auth/start",
|
|
13808
14209
|
cliRevokeSelf: "/api/v1/cli/revoke-self",
|
|
13809
14210
|
deviceCode: "/api/v1/cli/device-code",
|
|
13810
14211
|
deviceToken: "/api/v1/cli/device-token",
|
|
@@ -13890,32 +14291,10 @@ const resolveApiKey = (options) => {
|
|
|
13890
14291
|
//#endregion
|
|
13891
14292
|
//#region src/services/telemetry.ts
|
|
13892
14293
|
const TELEMETRY_TIMEOUT_MS = 500;
|
|
13893
|
-
const telemetryInstallationSchema = object({ installationId: uuid() }).strict();
|
|
13894
|
-
const loadOrCreateInstallationId = (telemetryPath, configDir, fileDeps) => {
|
|
13895
|
-
try {
|
|
13896
|
-
if (fileDeps.existsSyncFn(telemetryPath)) {
|
|
13897
|
-
const raw = fileDeps.readFileSyncFn(telemetryPath, "utf8");
|
|
13898
|
-
const parsed = telemetryInstallationSchema.safeParse(JSON.parse(raw));
|
|
13899
|
-
if (parsed.success) return parsed.data.installationId;
|
|
13900
|
-
}
|
|
13901
|
-
} catch {}
|
|
13902
|
-
const installationId = fileDeps.randomUuidFn();
|
|
13903
|
-
try {
|
|
13904
|
-
fileDeps.mkdirSyncFn(configDir, { recursive: true });
|
|
13905
|
-
fileDeps.writeFileSyncFn(telemetryPath, JSON.stringify({ installationId }, null, 2), { mode: 384 });
|
|
13906
|
-
} catch {}
|
|
13907
|
-
return installationId;
|
|
13908
|
-
};
|
|
13909
14294
|
const createTelemetryService = (deps) => {
|
|
13910
14295
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
13911
|
-
const
|
|
13912
|
-
|
|
13913
|
-
mkdirSyncFn: mkdirSync,
|
|
13914
|
-
randomUuidFn: randomUUID,
|
|
13915
|
-
readFileSyncFn: readFileSync,
|
|
13916
|
-
writeFileSyncFn: writeFileSync
|
|
13917
|
-
};
|
|
13918
|
-
const installationId = loadOrCreateInstallationId(deps.telemetryPath, deps.configDir, fileDeps);
|
|
14296
|
+
const installationIdResult = deps.authStateStore.getInstallationId();
|
|
14297
|
+
const installationId = Result.isOk(installationIdResult) ? installationIdResult.value : randomUUID();
|
|
13919
14298
|
const endpoint = `${deps.baseUrl}${cliEndpoints.telemetry}`;
|
|
13920
14299
|
return { trackCommandCompleted: async (input) => {
|
|
13921
14300
|
if (deps.telemetryDisabled) return;
|
|
@@ -13935,7 +14314,7 @@ const createTelemetryService = (deps) => {
|
|
|
13935
14314
|
if (!payload.success) throw new Error("Invalid CLI telemetry payload");
|
|
13936
14315
|
const apiKeyResolution = resolveApiKey({
|
|
13937
14316
|
apiKeyOverride: deps.getApiKeyOverride?.(),
|
|
13938
|
-
credentialsStore: deps.
|
|
14317
|
+
credentialsStore: deps.authStateStore,
|
|
13939
14318
|
envApiKey: deps.envApiKey
|
|
13940
14319
|
});
|
|
13941
14320
|
const headers = {
|
|
@@ -13966,6 +14345,7 @@ const EXIT_CODE = {
|
|
|
13966
14345
|
rateLimit: 6,
|
|
13967
14346
|
remote: 7,
|
|
13968
14347
|
success: 0,
|
|
14348
|
+
updateRequired: 10,
|
|
13969
14349
|
validation: 2
|
|
13970
14350
|
};
|
|
13971
14351
|
const exitCodeForError = (error) => {
|
|
@@ -13979,6 +14359,7 @@ const exitCodeForError = (error) => {
|
|
|
13979
14359
|
case ERROR_CATEGORY.remote:
|
|
13980
14360
|
case ERROR_CATEGORY.transport:
|
|
13981
14361
|
case ERROR_CATEGORY.authorization: return EXIT_CODE.remote;
|
|
14362
|
+
case ERROR_CATEGORY.updateRequired: return EXIT_CODE.updateRequired;
|
|
13982
14363
|
case ERROR_CATEGORY.contractMismatch: return EXIT_CODE.contractMismatch;
|
|
13983
14364
|
case ERROR_CATEGORY.validation: return EXIT_CODE.validation;
|
|
13984
14365
|
case ERROR_CATEGORY.internal: return EXIT_CODE.internal;
|
|
@@ -14558,6 +14939,9 @@ const ensureSuccess = (result, runtime, context) => {
|
|
|
14558
14939
|
const nextActionsForLogin = (payload, mode) => {
|
|
14559
14940
|
return [nextAction("check_auth_status", "Check auth status", cliCommands.auth.status(), `Verify persisted credentials after ${mode} login for ${payload.agentName}.`, "immediate"), nextAction("discover_servers", "Discover servers", cliCommands.catalog.command(), `Logged in as ${payload.agentName} via ${mode}; discover available servers.`, "after_auth")];
|
|
14560
14941
|
};
|
|
14942
|
+
const nextActionsForPendingLogin = (userCode, verificationUri) => {
|
|
14943
|
+
return [nextAction("check_login_status", "Check login status", cliCommands.auth.status(), `Approve ${userCode} at ${verificationUri}, then verify local auth state.`, "immediate"), nextAction("restart_device_login", "Restart login", cliCommands.auth.login(), `Restart login if code ${userCode} expires before approval.`, "if_expired")];
|
|
14944
|
+
};
|
|
14561
14945
|
const nextActionsForAuthStatus = (payload) => {
|
|
14562
14946
|
if (!payload.loggedIn) return [nextAction("login", "Authenticate", cliCommands.auth.login(), "No API key is configured locally; login is required.", "immediate")];
|
|
14563
14947
|
return [nextAction("discover_servers", "Discover servers", cliCommands.catalog.command(), `Authenticated via ${payload.apiKeySource}; discover servers next.`, "after_auth")];
|
|
@@ -14597,42 +14981,35 @@ const runLoginFlow = async (runtime, options) => {
|
|
|
14597
14981
|
commandOptions: options,
|
|
14598
14982
|
invokedCommand: cliCommands.auth.loginWithApiKeyRedacted(),
|
|
14599
14983
|
mode: "apiKey"
|
|
14600
|
-
} :
|
|
14601
|
-
|
|
14602
|
-
|
|
14603
|
-
|
|
14604
|
-
|
|
14605
|
-
onPending: ({ userCode, verificationUri }) => {
|
|
14606
|
-
const pendingNextActions = [nextAction("check_login_status", "Check login status", cliCommands.auth.status(), `Approve ${userCode} at ${verificationUri}, then verify local auth state.`, "immediate"), nextAction("restart_device_login", "Restart login", cliCommands.auth.login(), `Restart login if code ${userCode} expires before approval.`, "if_expired")];
|
|
14607
|
-
runtime.output.success({
|
|
14608
|
-
event: "auth_login.pending",
|
|
14609
|
-
loggedIn: false,
|
|
14610
|
-
verification: {
|
|
14611
|
-
userCode,
|
|
14612
|
-
verificationUri
|
|
14613
|
-
}
|
|
14614
|
-
}, {
|
|
14615
|
-
command: deviceCommand,
|
|
14616
|
-
entity: {
|
|
14617
|
-
event: "auth_login.pending",
|
|
14618
|
-
mode: "device",
|
|
14619
|
-
verification: {
|
|
14620
|
-
userCode,
|
|
14621
|
-
verificationUri
|
|
14622
|
-
}
|
|
14623
|
-
},
|
|
14624
|
-
nextActions: pendingNextActions
|
|
14625
|
-
});
|
|
14626
|
-
}
|
|
14627
|
-
},
|
|
14628
|
-
invokedCommand: deviceCommand,
|
|
14629
|
-
mode: "device"
|
|
14630
|
-
};
|
|
14631
|
-
})();
|
|
14984
|
+
} : {
|
|
14985
|
+
commandOptions: { mode: "device" },
|
|
14986
|
+
invokedCommand: cliCommands.auth.login(),
|
|
14987
|
+
mode: "device"
|
|
14988
|
+
};
|
|
14632
14989
|
const data = ensureSuccess(await runAuthLoginCommand(runtime.context, commandOptions), runtime, {
|
|
14633
14990
|
command: invokedCommand,
|
|
14634
14991
|
entity: { mode }
|
|
14635
14992
|
});
|
|
14993
|
+
if (!data.loggedIn) {
|
|
14994
|
+
const pendingOutput = {
|
|
14995
|
+
event: "auth_login.pending",
|
|
14996
|
+
loggedIn: false,
|
|
14997
|
+
verification: data.verification
|
|
14998
|
+
};
|
|
14999
|
+
runtime.output.success(pendingOutput, {
|
|
15000
|
+
command: invokedCommand,
|
|
15001
|
+
entity: {
|
|
15002
|
+
event: "auth_login.pending",
|
|
15003
|
+
mode,
|
|
15004
|
+
verification: {
|
|
15005
|
+
userCode: data.verification.userCode,
|
|
15006
|
+
verificationUri: data.verification.verificationUri
|
|
15007
|
+
}
|
|
15008
|
+
},
|
|
15009
|
+
nextActions: nextActionsForPendingLogin(data.verification.userCode, data.verification.verificationUri)
|
|
15010
|
+
});
|
|
15011
|
+
return;
|
|
15012
|
+
}
|
|
14636
15013
|
const outputData = {
|
|
14637
15014
|
...data,
|
|
14638
15015
|
event: "auth_login.completed"
|
|
@@ -60381,6 +60758,7 @@ const createCliSentry = (config) => {
|
|
|
60381
60758
|
break;
|
|
60382
60759
|
case "AuthError":
|
|
60383
60760
|
case "ContractMismatchError":
|
|
60761
|
+
case "UpdateRequiredError":
|
|
60384
60762
|
case "UnexpectedError":
|
|
60385
60763
|
case "ValidationError": break;
|
|
60386
60764
|
default: assertNever$1(error);
|
|
@@ -60441,103 +60819,136 @@ const createAccountService = (deps) => {
|
|
|
60441
60819
|
|
|
60442
60820
|
//#endregion
|
|
60443
60821
|
//#region src/services/auth.ts
|
|
60444
|
-
const
|
|
60445
|
-
await new Promise((resolve) => {
|
|
60446
|
-
setTimeout(resolve, milliseconds);
|
|
60447
|
-
});
|
|
60448
|
-
};
|
|
60822
|
+
const DEFAULT_NAMESPACE_KEY = "default";
|
|
60449
60823
|
const toEndpointUrl = (baseUrl, endpoint) => {
|
|
60450
60824
|
return `${baseUrl}${endpoint}`;
|
|
60451
60825
|
};
|
|
60452
|
-
const
|
|
60453
|
-
return toEndpointUrl(baseUrl, cliEndpoints.
|
|
60826
|
+
const authStartUrl = (baseUrl) => {
|
|
60827
|
+
return toEndpointUrl(baseUrl, cliEndpoints.authStart);
|
|
60454
60828
|
};
|
|
60455
|
-
const
|
|
60456
|
-
return toEndpointUrl(baseUrl, cliEndpoints.
|
|
60829
|
+
const authExchangeUrl = (baseUrl) => {
|
|
60830
|
+
return toEndpointUrl(baseUrl, cliEndpoints.authExchange);
|
|
60457
60831
|
};
|
|
60458
|
-
const
|
|
60459
|
-
|
|
60832
|
+
const isExpiredPendingRequest = (request, nowMs) => {
|
|
60833
|
+
const parsedExpiresAt = Date.parse(request.expiresAt);
|
|
60834
|
+
return !Number.isNaN(parsedExpiresAt) && parsedExpiresAt <= nowMs;
|
|
60460
60835
|
};
|
|
60461
60836
|
const createAuthService = (deps) => {
|
|
60462
60837
|
const now = deps.now ?? Date.now;
|
|
60463
|
-
const
|
|
60838
|
+
const storeApprovedAuth = (approvedAuth, namespaceKey = DEFAULT_NAMESPACE_KEY) => {
|
|
60839
|
+
return deps.credentialsStore.storeApprovedAuth({
|
|
60840
|
+
agentName: approvedAuth.agentName,
|
|
60841
|
+
apiKey: approvedAuth.apiKey
|
|
60842
|
+
}, namespaceKey);
|
|
60843
|
+
};
|
|
60844
|
+
const saveApiKeyAuth = (approvedAuth) => {
|
|
60845
|
+
return deps.credentialsStore.save({
|
|
60846
|
+
agentName: approvedAuth.agentName,
|
|
60847
|
+
apiKey: approvedAuth.apiKey
|
|
60848
|
+
});
|
|
60849
|
+
};
|
|
60850
|
+
const tryExchangePendingRequests = async () => {
|
|
60851
|
+
const installationIdResult = deps.credentialsStore.getInstallationId();
|
|
60852
|
+
if (Result.isError(installationIdResult)) return installationIdResult;
|
|
60853
|
+
const pendingRequestsResult = deps.credentialsStore.listPendingRequests();
|
|
60854
|
+
if (Result.isError(pendingRequestsResult)) return pendingRequestsResult;
|
|
60855
|
+
const pendingRequests = [...pendingRequestsResult.value].toSorted((a, b) => {
|
|
60856
|
+
return a.createdAt.localeCompare(b.createdAt);
|
|
60857
|
+
});
|
|
60858
|
+
for (const pendingRequest of pendingRequests) {
|
|
60859
|
+
if (isExpiredPendingRequest(pendingRequest, now())) {
|
|
60860
|
+
const deleteResult = deps.credentialsStore.deletePendingRequest(pendingRequest.authRequestId);
|
|
60861
|
+
if (Result.isError(deleteResult)) return deleteResult;
|
|
60862
|
+
continue;
|
|
60863
|
+
}
|
|
60864
|
+
const exchangePayload = await requestJson(deps.httpClient, authExchangeUrl(deps.baseUrl), authExchangeSchema, {
|
|
60865
|
+
body: JSON.stringify({
|
|
60866
|
+
authRequestId: pendingRequest.authRequestId,
|
|
60867
|
+
installationId: installationIdResult.value,
|
|
60868
|
+
namespaceKey: pendingRequest.namespaceKey
|
|
60869
|
+
}),
|
|
60870
|
+
headers: { "Content-Type": "application/json" },
|
|
60871
|
+
method: "POST"
|
|
60872
|
+
}, {
|
|
60873
|
+
context: "auth exchange response",
|
|
60874
|
+
mapStatusError: (response, body) => new RemoteRequestError({
|
|
60875
|
+
body,
|
|
60876
|
+
httpStatus: response.status,
|
|
60877
|
+
message: "Failed to exchange pending CLI auth",
|
|
60878
|
+
operation: "auth/exchange",
|
|
60879
|
+
raw: body,
|
|
60880
|
+
source: "http"
|
|
60881
|
+
}),
|
|
60882
|
+
operation: "auth/exchange"
|
|
60883
|
+
});
|
|
60884
|
+
if (Result.isError(exchangePayload)) return exchangePayload;
|
|
60885
|
+
const exchangeData = exchangePayload.value.data;
|
|
60886
|
+
if (exchangeData.status === "authorization_pending") continue;
|
|
60887
|
+
if (exchangeData.status === "approved") {
|
|
60888
|
+
const storeResult = storeApprovedAuth({
|
|
60889
|
+
agentName: exchangeData.agentName,
|
|
60890
|
+
apiKey: exchangeData.apiKey
|
|
60891
|
+
}, pendingRequest.namespaceKey);
|
|
60892
|
+
if (Result.isError(storeResult)) return storeResult;
|
|
60893
|
+
return Result.ok({
|
|
60894
|
+
agentName: exchangeData.agentName,
|
|
60895
|
+
apiKey: exchangeData.apiKey
|
|
60896
|
+
});
|
|
60897
|
+
}
|
|
60898
|
+
const deleteResult = deps.credentialsStore.deletePendingRequest(pendingRequest.authRequestId);
|
|
60899
|
+
if (Result.isError(deleteResult)) return deleteResult;
|
|
60900
|
+
}
|
|
60901
|
+
return Result.ok(null);
|
|
60902
|
+
};
|
|
60464
60903
|
const loginWithDevice = async (options) => {
|
|
60465
|
-
const
|
|
60904
|
+
const installationIdResult = deps.credentialsStore.getInstallationId();
|
|
60905
|
+
if (Result.isError(installationIdResult)) return installationIdResult;
|
|
60906
|
+
const startPayload = await requestJson(deps.httpClient, authStartUrl(deps.baseUrl), authStartSchema, {
|
|
60907
|
+
body: JSON.stringify({
|
|
60908
|
+
...deps.executionEnvironment === void 0 ? {} : { executionEnvironment: deps.executionEnvironment },
|
|
60909
|
+
installationId: installationIdResult.value,
|
|
60910
|
+
namespaceKey: DEFAULT_NAMESPACE_KEY
|
|
60911
|
+
}),
|
|
60466
60912
|
headers: { "Content-Type": "application/json" },
|
|
60467
60913
|
method: "POST"
|
|
60468
60914
|
}, {
|
|
60469
|
-
context: "
|
|
60915
|
+
context: "auth start response",
|
|
60470
60916
|
mapStatusError: (response, body) => new RemoteRequestError({
|
|
60471
60917
|
body,
|
|
60472
60918
|
httpStatus: response.status,
|
|
60473
|
-
message: "Failed to start
|
|
60474
|
-
operation: "auth/
|
|
60919
|
+
message: "Failed to start CLI login",
|
|
60920
|
+
operation: "auth/start",
|
|
60475
60921
|
raw: body,
|
|
60476
60922
|
source: "http"
|
|
60477
60923
|
}),
|
|
60478
|
-
operation: "auth/
|
|
60924
|
+
operation: "auth/start"
|
|
60479
60925
|
});
|
|
60480
60926
|
if (Result.isError(startPayload)) return startPayload;
|
|
60481
|
-
|
|
60927
|
+
const savePendingRequestResult = deps.credentialsStore.savePendingRequest({
|
|
60928
|
+
authRequestId: startPayload.value.data.authRequestId,
|
|
60929
|
+
code: startPayload.value.data.userCode,
|
|
60930
|
+
createdAt: new Date(now()).toISOString(),
|
|
60931
|
+
expiresAt: startPayload.value.data.expiresAt,
|
|
60932
|
+
namespaceKey: DEFAULT_NAMESPACE_KEY,
|
|
60933
|
+
verificationUrl: startPayload.value.data.verificationUrl
|
|
60934
|
+
});
|
|
60935
|
+
if (Result.isError(savePendingRequestResult)) return savePendingRequestResult;
|
|
60936
|
+
const verification = {
|
|
60482
60937
|
userCode: startPayload.value.data.userCode,
|
|
60483
|
-
verificationUri: startPayload.value.data.
|
|
60938
|
+
verificationUri: startPayload.value.data.verificationUrl
|
|
60939
|
+
};
|
|
60940
|
+
options.onPending?.(verification);
|
|
60941
|
+
return Result.ok({
|
|
60942
|
+
agentName: null,
|
|
60943
|
+
loggedIn: false,
|
|
60944
|
+
outcome: "pending_approval",
|
|
60945
|
+
verification
|
|
60484
60946
|
});
|
|
60485
|
-
const deadline = now() + startPayload.value.data.expiresIn * 1e3;
|
|
60486
|
-
const interval = startPayload.value.data.interval * 1e3;
|
|
60487
|
-
while (now() < deadline) {
|
|
60488
|
-
await sleep(interval);
|
|
60489
|
-
const pollPayload = await requestJson(deps.httpClient, deviceTokenUrl(deps.baseUrl), deviceTokenSchema, {
|
|
60490
|
-
body: JSON.stringify({ deviceCode: startPayload.value.data.deviceCode }),
|
|
60491
|
-
headers: { "Content-Type": "application/json" },
|
|
60492
|
-
method: "POST"
|
|
60493
|
-
}, {
|
|
60494
|
-
context: "device login poll response",
|
|
60495
|
-
mapStatusError: (response, body) => {
|
|
60496
|
-
if (response.status === 410) return new AuthError({
|
|
60497
|
-
code: ERROR_CODE.authDeviceExpired,
|
|
60498
|
-
message: "Device login code expired. Run `ogment auth login` again.",
|
|
60499
|
-
recovery: { command: "ogment auth login" }
|
|
60500
|
-
});
|
|
60501
|
-
if (response.status === 404) return new AuthError({
|
|
60502
|
-
code: ERROR_CODE.authInvalidCredentials,
|
|
60503
|
-
message: "Invalid device login code. Run `ogment auth login` again.",
|
|
60504
|
-
recovery: { command: "ogment auth login" }
|
|
60505
|
-
});
|
|
60506
|
-
return new RemoteRequestError({
|
|
60507
|
-
body,
|
|
60508
|
-
httpStatus: response.status,
|
|
60509
|
-
message: "Failed to poll device login status",
|
|
60510
|
-
operation: "auth/device/poll",
|
|
60511
|
-
raw: body,
|
|
60512
|
-
source: "http"
|
|
60513
|
-
});
|
|
60514
|
-
},
|
|
60515
|
-
operation: "auth/device/poll"
|
|
60516
|
-
});
|
|
60517
|
-
if (Result.isError(pollPayload)) return pollPayload;
|
|
60518
|
-
if (pollPayload.value.data.status === "authorization_pending") continue;
|
|
60519
|
-
const approvedPayload = pollPayload.value.data;
|
|
60520
|
-
const saveResult = deps.credentialsStore.save({
|
|
60521
|
-
agentName: approvedPayload.agentName,
|
|
60522
|
-
apiKey: approvedPayload.apiKey
|
|
60523
|
-
});
|
|
60524
|
-
if (Result.isError(saveResult)) return saveResult;
|
|
60525
|
-
return Result.ok({
|
|
60526
|
-
agentName: approvedPayload.agentName,
|
|
60527
|
-
loggedIn: true,
|
|
60528
|
-
outcome: "authenticated"
|
|
60529
|
-
});
|
|
60530
|
-
}
|
|
60531
|
-
return Result.err(new AuthError({
|
|
60532
|
-
code: ERROR_CODE.authDeviceExpired,
|
|
60533
|
-
message: "Device login code expired. Run `ogment auth login` again.",
|
|
60534
|
-
recovery: { command: "ogment auth login" }
|
|
60535
|
-
}));
|
|
60536
60947
|
};
|
|
60537
60948
|
return {
|
|
60538
60949
|
login: async (options) => {
|
|
60539
60950
|
if (options.mode === "apiKey" && options.apiKey.length > 0) {
|
|
60540
|
-
const saveResult =
|
|
60951
|
+
const saveResult = saveApiKeyAuth({
|
|
60541
60952
|
agentName: "CLI Agent",
|
|
60542
60953
|
apiKey: options.apiKey
|
|
60543
60954
|
});
|
|
@@ -60560,25 +60971,29 @@ const createAuthService = (deps) => {
|
|
|
60560
60971
|
loggedIn: true,
|
|
60561
60972
|
outcome: "already_authenticated"
|
|
60562
60973
|
});
|
|
60974
|
+
const exchangedPendingAuth = await tryExchangePendingRequests();
|
|
60975
|
+
if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
|
|
60976
|
+
if (exchangedPendingAuth.value !== null) return Result.ok({
|
|
60977
|
+
agentName: exchangedPendingAuth.value.agentName,
|
|
60978
|
+
loggedIn: true,
|
|
60979
|
+
outcome: "authenticated"
|
|
60980
|
+
});
|
|
60563
60981
|
return loginWithDevice(options);
|
|
60564
60982
|
},
|
|
60565
60983
|
logout: async () => {
|
|
60566
60984
|
const stored = deps.credentialsStore.load();
|
|
60567
60985
|
if (Result.isError(stored)) return stored;
|
|
60568
|
-
|
|
60986
|
+
const pendingRequestsResult = deps.credentialsStore.listPendingRequests();
|
|
60987
|
+
if (Result.isError(pendingRequestsResult)) return pendingRequestsResult;
|
|
60988
|
+
if (!(stored.value !== null || pendingRequestsResult.value.length > 0)) return Result.ok({
|
|
60569
60989
|
localCredentialsDeleted: false,
|
|
60570
60990
|
revoked: false
|
|
60571
60991
|
});
|
|
60572
|
-
const revokeResult = await deps.httpClient.request(revokeUrl(deps.baseUrl), {
|
|
60573
|
-
headers: { Authorization: `Bearer ${stored.value.apiKey}` },
|
|
60574
|
-
method: "POST"
|
|
60575
|
-
});
|
|
60576
60992
|
const deleteResult = deps.credentialsStore.delete();
|
|
60577
60993
|
if (Result.isError(deleteResult)) return deleteResult;
|
|
60578
|
-
const revoked = Result.isOk(revokeResult) && revokeResult.value.ok;
|
|
60579
60994
|
return Result.ok({
|
|
60580
60995
|
localCredentialsDeleted: true,
|
|
60581
|
-
revoked
|
|
60996
|
+
revoked: false
|
|
60582
60997
|
});
|
|
60583
60998
|
},
|
|
60584
60999
|
resolveApiKey: async (overrideApiKey) => {
|
|
@@ -60589,6 +61004,9 @@ const createAuthService = (deps) => {
|
|
|
60589
61004
|
});
|
|
60590
61005
|
if (resolution.apiKey !== null) return Result.ok(resolution.apiKey);
|
|
60591
61006
|
if (resolution.source === "credentialsError" && resolution.loadError) return Result.err(resolution.loadError);
|
|
61007
|
+
const exchangedPendingAuth = await tryExchangePendingRequests();
|
|
61008
|
+
if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
|
|
61009
|
+
if (exchangedPendingAuth.value !== null) return Result.ok(exchangedPendingAuth.value.apiKey);
|
|
60592
61010
|
return Result.err(new AuthError({
|
|
60593
61011
|
code: ERROR_CODE.authRequired,
|
|
60594
61012
|
message: "Not logged in. Run `ogment auth login` or set OGMENT_API_KEY.",
|
|
@@ -60602,17 +61020,26 @@ const createAuthService = (deps) => {
|
|
|
60602
61020
|
envApiKey: deps.envApiKey
|
|
60603
61021
|
});
|
|
60604
61022
|
if (resolution.source === "credentialsError" && resolution.loadError) return Result.err(resolution.loadError);
|
|
60605
|
-
if (resolution.apiKey
|
|
61023
|
+
if (resolution.apiKey !== null) {
|
|
61024
|
+
const apiKeySource = resolution.source === "credentialsFile" ? "credentialsFile" : resolution.source === "apiKeyOption" ? "apiKeyOption" : "env";
|
|
61025
|
+
return Result.ok({
|
|
61026
|
+
agentName: resolution.agentName,
|
|
61027
|
+
apiKeySource,
|
|
61028
|
+
loggedIn: true
|
|
61029
|
+
});
|
|
61030
|
+
}
|
|
61031
|
+
const exchangedPendingAuth = await tryExchangePendingRequests();
|
|
61032
|
+
if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
|
|
61033
|
+
if (exchangedPendingAuth.value !== null) return Result.ok({
|
|
61034
|
+
agentName: exchangedPendingAuth.value.agentName,
|
|
61035
|
+
apiKeySource: "credentialsFile",
|
|
61036
|
+
loggedIn: true
|
|
61037
|
+
});
|
|
61038
|
+
return Result.ok({
|
|
60606
61039
|
agentName: null,
|
|
60607
61040
|
apiKeySource: "none",
|
|
60608
61041
|
loggedIn: false
|
|
60609
61042
|
});
|
|
60610
|
-
const apiKeySource = resolution.source === "credentialsFile" ? "credentialsFile" : resolution.source === "apiKeyOption" ? "apiKeyOption" : "env";
|
|
60611
|
-
return Result.ok({
|
|
60612
|
-
agentName: resolution.agentName,
|
|
60613
|
-
apiKeySource,
|
|
60614
|
-
loggedIn: true
|
|
60615
|
-
});
|
|
60616
61043
|
}
|
|
60617
61044
|
};
|
|
60618
61045
|
};
|
|
@@ -60626,7 +61053,7 @@ const maskApiKey = (apiKey) => {
|
|
|
60626
61053
|
const nextActionByIssueCode = (includeDebug) => {
|
|
60627
61054
|
return {
|
|
60628
61055
|
auth_failed: "Run `ogment auth login` to refresh credentials.",
|
|
60629
|
-
credentials_load_failed: "Check file permissions and contents of `~/.config/ogment/
|
|
61056
|
+
credentials_load_failed: "Check file permissions and contents of `~/.config/ogment/auth-state.json`.",
|
|
60630
61057
|
no_api_key: "Run `ogment auth login` or set `OGMENT_API_KEY`.",
|
|
60631
61058
|
unreachable: includeDebug ? "Verify `OGMENT_BASE_URL` and network connectivity." : "Verify network connectivity."
|
|
60632
61059
|
};
|
|
@@ -60756,11 +61183,11 @@ const createInfoService = (deps) => {
|
|
|
60756
61183
|
});
|
|
60757
61184
|
if (apiKeyResolution.source === "none") issues.push({
|
|
60758
61185
|
code: "no_api_key",
|
|
60759
|
-
message: "No API key found in --api-key, OGMENT_API_KEY, or
|
|
61186
|
+
message: "No API key found in --api-key, OGMENT_API_KEY, or local auth state."
|
|
60760
61187
|
});
|
|
60761
61188
|
if (apiKeyResolution.source === "credentialsError" && apiKeyResolution.loadError !== null) issues.push({
|
|
60762
61189
|
code: "credentials_load_failed",
|
|
60763
|
-
message: `Failed to load
|
|
61190
|
+
message: `Failed to load local auth state: ${apiKeyResolution.loadError.message}`
|
|
60764
61191
|
});
|
|
60765
61192
|
if (account.status === "auth_error") issues.push({
|
|
60766
61193
|
code: "auth_failed",
|
|
@@ -60796,7 +61223,7 @@ const createInfoService = (deps) => {
|
|
|
60796
61223
|
configPrecedence: [
|
|
60797
61224
|
"--api-key option",
|
|
60798
61225
|
"OGMENT_API_KEY environment variable",
|
|
60799
|
-
"~/.config/ogment/
|
|
61226
|
+
"~/.config/ogment/auth-state.json",
|
|
60800
61227
|
...includeDebug ? ["default base URL when OGMENT_BASE_URL is unset"] : []
|
|
60801
61228
|
],
|
|
60802
61229
|
quickCommands: [
|
|
@@ -71591,9 +72018,11 @@ const createRuntime = () => {
|
|
|
71591
72018
|
const runtimeConfig = createRuntimeConfig();
|
|
71592
72019
|
const executionEnvironment = runtimeConfig.executionEnvironment;
|
|
71593
72020
|
const output = new OutputManager();
|
|
71594
|
-
const
|
|
72021
|
+
const authStateStore = createFileCredentialsStore({
|
|
71595
72022
|
configDir: runtimeConfig.configDir,
|
|
71596
|
-
credentialsPath: runtimeConfig.credentialsPath
|
|
72023
|
+
credentialsPath: runtimeConfig.credentialsPath,
|
|
72024
|
+
legacyCredentialsPath: runtimeConfig.legacyCredentialsPath,
|
|
72025
|
+
telemetryPath: runtimeConfig.telemetryPath
|
|
71597
72026
|
});
|
|
71598
72027
|
const httpClient = createHttpClient();
|
|
71599
72028
|
const services = {
|
|
@@ -71603,8 +72032,9 @@ const createRuntime = () => {
|
|
|
71603
72032
|
}),
|
|
71604
72033
|
auth: createAuthService({
|
|
71605
72034
|
baseUrl: runtimeConfig.baseUrl,
|
|
71606
|
-
credentialsStore,
|
|
72035
|
+
credentialsStore: authStateStore,
|
|
71607
72036
|
envApiKey: runtimeConfig.envApiKey,
|
|
72037
|
+
executionEnvironment,
|
|
71608
72038
|
httpClient
|
|
71609
72039
|
}),
|
|
71610
72040
|
mcp: createMcpService({
|
|
@@ -71618,7 +72048,7 @@ const createRuntime = () => {
|
|
|
71618
72048
|
baseUrlSource: runtimeConfig.baseUrlSource,
|
|
71619
72049
|
configDir: runtimeConfig.configDir,
|
|
71620
72050
|
credentialsPath: runtimeConfig.credentialsPath,
|
|
71621
|
-
credentialsStore,
|
|
72051
|
+
credentialsStore: authStateStore,
|
|
71622
72052
|
envApiKey: runtimeConfig.envApiKey,
|
|
71623
72053
|
httpClient,
|
|
71624
72054
|
version: runtimeConfig.version
|
|
@@ -71639,18 +72069,16 @@ const createRuntime = () => {
|
|
|
71639
72069
|
output,
|
|
71640
72070
|
sentry,
|
|
71641
72071
|
telemetry: createTelemetryService({
|
|
72072
|
+
authStateStore,
|
|
71642
72073
|
baseUrl: runtimeConfig.baseUrl,
|
|
71643
72074
|
cliVersion: runtimeConfig.version,
|
|
71644
|
-
configDir: runtimeConfig.configDir,
|
|
71645
|
-
credentialsStore,
|
|
71646
72075
|
envApiKey: runtimeConfig.envApiKey,
|
|
71647
72076
|
executionEnvironment,
|
|
71648
72077
|
getApiKeyOverride: () => {
|
|
71649
72078
|
return context.apiKeyOverride;
|
|
71650
72079
|
},
|
|
71651
72080
|
httpClient,
|
|
71652
|
-
telemetryDisabled: runtimeConfig.telemetryDisabled
|
|
71653
|
-
telemetryPath: runtimeConfig.telemetryPath
|
|
72081
|
+
telemetryDisabled: runtimeConfig.telemetryDisabled
|
|
71654
72082
|
})
|
|
71655
72083
|
};
|
|
71656
72084
|
};
|
|
@@ -71792,6 +72220,7 @@ const commandFromArgv = (argv) => {
|
|
|
71792
72220
|
};
|
|
71793
72221
|
const RUNTIME_ERROR_COMMAND_PATH = "ogment <runtime_error>";
|
|
71794
72222
|
const PARSE_ERROR_COMMAND_PATH = "ogment <parse_error>";
|
|
72223
|
+
const CLI_PACKAGE_NAME = "@ogment-ai/cli";
|
|
71795
72224
|
const PARSE_ERROR_SCOPE_COMMAND_PATHS = {
|
|
71796
72225
|
auth: "ogment auth <parse_error>",
|
|
71797
72226
|
catalog: "ogment catalog <parse_error>",
|
|
@@ -71888,22 +72317,42 @@ const telemetryErrorDetail = (error) => {
|
|
|
71888
72317
|
if (error instanceof Error) return `${error.name}: ${error.message}`;
|
|
71889
72318
|
return "Unknown telemetry error";
|
|
71890
72319
|
};
|
|
72320
|
+
function updateInstallCommand(packageName) {
|
|
72321
|
+
return `npm i -g ${packageName}`;
|
|
72322
|
+
}
|
|
72323
|
+
function formatUpdateRequiredDetail(update) {
|
|
72324
|
+
return [`Update the Ogment CLI before retrying this command (current: v${update.currentVersion}; latest: v${update.latestVersion}).`, `Run \`${updateInstallCommand(update.packageName)}\` to update, or set \`${OGMENT_NO_UPDATE_CHECK_ENV_VAR}=1\` or \`${NO_UPDATE_NOTIFIER_ENV_VAR}=1\` to bypass this check.`].join(" ");
|
|
72325
|
+
}
|
|
72326
|
+
function createUpdateRequiredError(update) {
|
|
72327
|
+
return new UpdateRequiredError({
|
|
72328
|
+
currentVersion: update.currentVersion,
|
|
72329
|
+
latestVersion: update.latestVersion,
|
|
72330
|
+
message: formatUpdateRequiredDetail(update),
|
|
72331
|
+
packageName: update.packageName,
|
|
72332
|
+
recovery: {
|
|
72333
|
+
command: updateInstallCommand(update.packageName),
|
|
72334
|
+
id: "update_cli",
|
|
72335
|
+
reason: "A newer Ogment CLI version must be installed before this command can run.",
|
|
72336
|
+
title: "Update CLI",
|
|
72337
|
+
when: "immediate"
|
|
72338
|
+
}
|
|
72339
|
+
});
|
|
72340
|
+
}
|
|
71891
72341
|
const emitRuntimeErrorTelemetry = async (output, input) => {
|
|
71892
72342
|
const telemetryConfig = createTelemetryRuntimeConfig();
|
|
71893
|
-
const credentialsStore = createFileCredentialsStore({
|
|
71894
|
-
configDir: telemetryConfig.configDir,
|
|
71895
|
-
credentialsPath: telemetryConfig.credentialsPath
|
|
71896
|
-
});
|
|
71897
72343
|
const telemetry = createTelemetryService({
|
|
72344
|
+
authStateStore: createFileCredentialsStore({
|
|
72345
|
+
configDir: telemetryConfig.configDir,
|
|
72346
|
+
credentialsPath: telemetryConfig.credentialsPath,
|
|
72347
|
+
legacyCredentialsPath: telemetryConfig.legacyCredentialsPath,
|
|
72348
|
+
telemetryPath: telemetryConfig.telemetryPath
|
|
72349
|
+
}),
|
|
71898
72350
|
baseUrl: telemetryConfig.baseUrl,
|
|
71899
72351
|
cliVersion: telemetryConfig.version,
|
|
71900
|
-
configDir: telemetryConfig.configDir,
|
|
71901
|
-
credentialsStore,
|
|
71902
72352
|
envApiKey: telemetryConfig.envApiKey,
|
|
71903
72353
|
executionEnvironment: telemetryConfig.executionEnvironment,
|
|
71904
72354
|
httpClient: createHttpClient(),
|
|
71905
|
-
telemetryDisabled: telemetryConfig.telemetryDisabled
|
|
71906
|
-
telemetryPath: telemetryConfig.telemetryPath
|
|
72355
|
+
telemetryDisabled: telemetryConfig.telemetryDisabled
|
|
71907
72356
|
});
|
|
71908
72357
|
try {
|
|
71909
72358
|
await telemetry.trackCommandCompleted({
|
|
@@ -71954,12 +72403,17 @@ const commandContextFromInvocation = (invocation, apiKeyOverride) => {
|
|
|
71954
72403
|
default: return assertNever(invocation);
|
|
71955
72404
|
}
|
|
71956
72405
|
};
|
|
71957
|
-
const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
|
|
72406
|
+
const runCli = async (argv = process.argv.slice(2), runtime = void 0, deps = {}) => {
|
|
71958
72407
|
const normalizedArgv = normalizeCliArgv(argv);
|
|
71959
72408
|
const outputOptions = parseOutputOptionsFromArgv(normalizedArgv);
|
|
71960
72409
|
const displayCommand = commandFromArgv(argv);
|
|
71961
72410
|
const startedAt = Date.now();
|
|
71962
|
-
const
|
|
72411
|
+
const shouldCheckForUpdate = runtime === void 0 || deps.checkForCliUpdate !== void 0;
|
|
72412
|
+
const checkForCliUpdate = deps.checkForCliUpdate ?? maybeGetAvailableCliUpdate;
|
|
72413
|
+
const updateCheckOptions = {
|
|
72414
|
+
currentVersion: VERSION$2,
|
|
72415
|
+
packageName: CLI_PACKAGE_NAME
|
|
72416
|
+
};
|
|
71963
72417
|
let telemetryContext = null;
|
|
71964
72418
|
let telemetryErrorCode = null;
|
|
71965
72419
|
let shouldEmitTelemetry = true;
|
|
@@ -71991,10 +72445,6 @@ const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
|
|
|
71991
72445
|
executionEnvironment: resolvedRuntime.executionEnvironment
|
|
71992
72446
|
});
|
|
71993
72447
|
};
|
|
71994
|
-
if (runtimeCreatedInternally) await maybeNotifyCliUpdate({
|
|
71995
|
-
currentVersion: VERSION$2,
|
|
71996
|
-
packageName: "@ogment-ai/cli"
|
|
71997
|
-
});
|
|
71998
72448
|
const parseState = createCommanderParseState();
|
|
71999
72449
|
const { program, readInvocation } = createProgram(resolvedRuntime, parseState);
|
|
72000
72450
|
try {
|
|
@@ -72004,12 +72454,23 @@ const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
|
|
|
72004
72454
|
if (invocation !== null) {
|
|
72005
72455
|
resolvedInvocation = invocation;
|
|
72006
72456
|
telemetryContext = telemetryContextFromInvocation(invocation);
|
|
72007
|
-
await
|
|
72457
|
+
const availableUpdate = shouldCheckForUpdate ? await checkForCliUpdate(updateCheckOptions) : null;
|
|
72458
|
+
if (availableUpdate !== null) {
|
|
72459
|
+
const updateError = createUpdateRequiredError(availableUpdate);
|
|
72460
|
+
resolvedRuntime.output.error(updateError, {
|
|
72461
|
+
command: displayCommand,
|
|
72462
|
+
entity: null
|
|
72463
|
+
});
|
|
72464
|
+
telemetryErrorCode = ERROR_CODE.cliUpdateRequired;
|
|
72465
|
+
finalExitCode = exitCodeForError(updateError);
|
|
72466
|
+
} else {
|
|
72467
|
+
await executeInvocation(resolvedRuntime, invocation);
|
|
72468
|
+
finalExitCode = EXIT_CODE.success;
|
|
72469
|
+
}
|
|
72008
72470
|
} else {
|
|
72009
72471
|
shouldEmitTelemetry = false;
|
|
72010
72472
|
resolvedRuntime.output.info("[telemetry] command_completed event not sent: missing telemetry context");
|
|
72011
72473
|
}
|
|
72012
|
-
finalExitCode = EXIT_CODE.success;
|
|
72013
72474
|
} catch (error) {
|
|
72014
72475
|
if (error instanceof CliExitError) {
|
|
72015
72476
|
if (error.error !== void 0) captureCliError(error.error);
|
|
@@ -72089,4 +72550,4 @@ if (shouldExecuteCli(import.meta.url, process.argv[1])) await executeCli();
|
|
|
72089
72550
|
|
|
72090
72551
|
//#endregion
|
|
72091
72552
|
export { executeCli, runCli, shouldExecuteCli };
|
|
72092
|
-
//# debugId=
|
|
72553
|
+
//# debugId=856f5502-c592-445c-b0d7-d8c7048e63ba
|