@ogment-ai/cli 0.8.2 → 0.9.1-next.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 +10 -12
- package/dist/cli.js +1090 -623
- 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
|
}
|
|
@@ -8419,9 +8798,9 @@ const DEFAULT_OGMENT_BASE_URL = "https://dashboard.ogment.ai";
|
|
|
8419
8798
|
* These are injected by Rolldown `transform.define` during release builds.
|
|
8420
8799
|
* Runtime env vars (`OGMENT_CLI_SENTRY_*`) can still override them.
|
|
8421
8800
|
*/
|
|
8422
|
-
const SENTRY_DSN_BUILD =
|
|
8423
|
-
const SENTRY_ENVIRONMENT_BUILD =
|
|
8424
|
-
const SENTRY_RELEASE_BUILD =
|
|
8801
|
+
const SENTRY_DSN_BUILD = void 0;
|
|
8802
|
+
const SENTRY_ENVIRONMENT_BUILD = void 0;
|
|
8803
|
+
const SENTRY_RELEASE_BUILD = void 0;
|
|
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,18 +13731,24 @@ 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()
|
|
13357
13739
|
});
|
|
13740
|
+
const isPrereleaseVersion = (version) => {
|
|
13741
|
+
const parsed = import_semver.default.parse(version);
|
|
13742
|
+
return parsed?.prerelease.length !== void 0 && parsed.prerelease.length > 0;
|
|
13743
|
+
};
|
|
13358
13744
|
const isTruthyEnvValue = (value) => {
|
|
13359
13745
|
if (value === void 0) return false;
|
|
13360
13746
|
const normalized = value.trim().toLowerCase();
|
|
13361
13747
|
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
13362
13748
|
};
|
|
13363
13749
|
const shouldSkipUpdateCheck = (env) => {
|
|
13364
|
-
if (isTruthyEnvValue(env[
|
|
13365
|
-
if (isTruthyEnvValue(env[
|
|
13750
|
+
if (isTruthyEnvValue(env[OGMENT_NO_UPDATE_CHECK_ENV_VAR])) return true;
|
|
13751
|
+
if (isTruthyEnvValue(env[NO_UPDATE_NOTIFIER_ENV_VAR])) return true;
|
|
13366
13752
|
if (isTruthyEnvValue(env["CI"])) return true;
|
|
13367
13753
|
return false;
|
|
13368
13754
|
};
|
|
@@ -13406,33 +13792,18 @@ const isNewerVersion = (latestVersion, currentVersion) => {
|
|
|
13406
13792
|
if (latest === null || current === null) return false;
|
|
13407
13793
|
return import_semver.default.gt(latest, current);
|
|
13408
13794
|
};
|
|
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
13795
|
const defaultConfigDir = () => {
|
|
13425
13796
|
return join(homedir(), ".config", "ogment");
|
|
13426
13797
|
};
|
|
13427
13798
|
const cachePathFromConfigDir = (configDir) => {
|
|
13428
13799
|
return join(configDir, UPDATE_CHECK_CACHE_FILENAME);
|
|
13429
13800
|
};
|
|
13430
|
-
const
|
|
13801
|
+
const maybeGetAvailableCliUpdate = async (options, deps = {}) => {
|
|
13431
13802
|
try {
|
|
13432
|
-
if (shouldSkipUpdateCheck(options.env ?? process.env)) return;
|
|
13803
|
+
if (shouldSkipUpdateCheck(options.env ?? process.env)) return null;
|
|
13804
|
+
if (isPrereleaseVersion(options.currentVersion)) return null;
|
|
13433
13805
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
13434
13806
|
const fetchLatestVersion = deps.fetchLatestVersion ?? defaultFetchLatestVersion;
|
|
13435
|
-
const stderr = options.stderr ?? process.stderr;
|
|
13436
13807
|
const cachePath = cachePathFromConfigDir(options.configDir ?? defaultConfigDir());
|
|
13437
13808
|
let latestVersion;
|
|
13438
13809
|
const existingCache = readCache(cachePath);
|
|
@@ -13448,8 +13819,13 @@ const maybeNotifyCliUpdate = async (options, deps = {}) => {
|
|
|
13448
13819
|
lastCheckedAtMs: nowMs
|
|
13449
13820
|
});
|
|
13450
13821
|
} else latestVersion = existingCache.latestVersion;
|
|
13451
|
-
if (latestVersion !== void 0 && isNewerVersion(latestVersion, options.currentVersion))
|
|
13822
|
+
if (latestVersion !== void 0 && isNewerVersion(latestVersion, options.currentVersion)) return {
|
|
13823
|
+
currentVersion: options.currentVersion,
|
|
13824
|
+
latestVersion,
|
|
13825
|
+
packageName: options.packageName
|
|
13826
|
+
};
|
|
13452
13827
|
} catch {}
|
|
13828
|
+
return null;
|
|
13453
13829
|
};
|
|
13454
13830
|
|
|
13455
13831
|
//#endregion
|
|
@@ -13741,6 +14117,7 @@ var OutputManager = class {
|
|
|
13741
14117
|
//#endregion
|
|
13742
14118
|
//#region ../../packages/shared/src/cli/common.ts
|
|
13743
14119
|
const jsonObjectSchema = record(string(), unknown());
|
|
14120
|
+
const isoTimestampSchema = datetime({ offset: true });
|
|
13744
14121
|
const cliErrorSchema = object({
|
|
13745
14122
|
code: string().min(1),
|
|
13746
14123
|
message: string().min(1)
|
|
@@ -13789,6 +14166,13 @@ const deviceCodeStartDataSchema = object({
|
|
|
13789
14166
|
verificationUri: string().url()
|
|
13790
14167
|
}).strict();
|
|
13791
14168
|
const deviceCodeStartSchema = cliSuccessEnvelopeSchema(deviceCodeStartDataSchema);
|
|
14169
|
+
const authStartDataSchema = object({
|
|
14170
|
+
authRequestId: string().min(1),
|
|
14171
|
+
expiresAt: isoTimestampSchema,
|
|
14172
|
+
userCode: string().min(1),
|
|
14173
|
+
verificationUrl: string().url()
|
|
14174
|
+
}).strict();
|
|
14175
|
+
const authStartSchema = cliSuccessEnvelopeSchema(authStartDataSchema);
|
|
13792
14176
|
const deviceTokenPendingDataSchema = object({ status: literal("authorization_pending") }).strict();
|
|
13793
14177
|
const deviceTokenApprovedDataSchema = object({
|
|
13794
14178
|
agentName: string().min(1),
|
|
@@ -13798,6 +14182,27 @@ const deviceTokenApprovedDataSchema = object({
|
|
|
13798
14182
|
const deviceTokenDataSchema = discriminatedUnion("status", [deviceTokenPendingDataSchema, deviceTokenApprovedDataSchema]);
|
|
13799
14183
|
const deviceTokenSchema = cliSuccessEnvelopeSchema(deviceTokenDataSchema);
|
|
13800
14184
|
const deviceTokenApprovedSchema = cliSuccessEnvelopeSchema(deviceTokenApprovedDataSchema);
|
|
14185
|
+
const authExchangePendingDataSchema = object({ status: literal("authorization_pending") }).strict();
|
|
14186
|
+
const authExchangeApprovedDataSchema = object({
|
|
14187
|
+
agentName: string().min(1),
|
|
14188
|
+
apiKey: string().min(1),
|
|
14189
|
+
scopeFingerprint: string().min(1),
|
|
14190
|
+
status: literal("approved")
|
|
14191
|
+
}).strict();
|
|
14192
|
+
const authExchangeExpiredDataSchema = object({ status: literal("expired") }).strict();
|
|
14193
|
+
const authExchangeDeniedDataSchema = object({ status: literal("denied") }).strict();
|
|
14194
|
+
const authExchangeConflictDataSchema = object({ status: literal("conflict") }).strict();
|
|
14195
|
+
const authExchangeInvalidDataSchema = object({ status: literal("invalid") }).strict();
|
|
14196
|
+
const authExchangeDataSchema = discriminatedUnion("status", [
|
|
14197
|
+
authExchangePendingDataSchema,
|
|
14198
|
+
authExchangeApprovedDataSchema,
|
|
14199
|
+
authExchangeExpiredDataSchema,
|
|
14200
|
+
authExchangeDeniedDataSchema,
|
|
14201
|
+
authExchangeConflictDataSchema,
|
|
14202
|
+
authExchangeInvalidDataSchema
|
|
14203
|
+
]);
|
|
14204
|
+
const authExchangeSchema = cliSuccessEnvelopeSchema(authExchangeDataSchema);
|
|
14205
|
+
const authExchangeApprovedSchema = cliSuccessEnvelopeSchema(authExchangeApprovedDataSchema);
|
|
13801
14206
|
const revokeSelfDataSchema = object({ success: literal(true) }).strict();
|
|
13802
14207
|
const revokeSelfSchema = cliSuccessEnvelopeSchema(revokeSelfDataSchema);
|
|
13803
14208
|
|
|
@@ -13805,6 +14210,8 @@ const revokeSelfSchema = cliSuccessEnvelopeSchema(revokeSelfDataSchema);
|
|
|
13805
14210
|
//#region ../../packages/shared/src/cli/endpoints.ts
|
|
13806
14211
|
const cliEndpoints = {
|
|
13807
14212
|
accountMe: "/api/v1/cli/me",
|
|
14213
|
+
authExchange: "/api/v1/cli/auth/exchange",
|
|
14214
|
+
authStart: "/api/v1/cli/auth/start",
|
|
13808
14215
|
cliRevokeSelf: "/api/v1/cli/revoke-self",
|
|
13809
14216
|
deviceCode: "/api/v1/cli/device-code",
|
|
13810
14217
|
deviceToken: "/api/v1/cli/device-token",
|
|
@@ -13843,7 +14250,7 @@ const cliCommandCompletedEventSchema = object({
|
|
|
13843
14250
|
inputMode: cliInputModeSchema.nullable(),
|
|
13844
14251
|
installationId: uuid(),
|
|
13845
14252
|
success: boolean(),
|
|
13846
|
-
timestamp:
|
|
14253
|
+
timestamp: isoTimestampSchema
|
|
13847
14254
|
}).strict();
|
|
13848
14255
|
const cliTelemetryAcceptedDataSchema = object({ accepted: literal(true) }).strict();
|
|
13849
14256
|
const cliTelemetryAcceptedSchema = cliSuccessEnvelopeSchema(cliTelemetryAcceptedDataSchema);
|
|
@@ -13890,32 +14297,10 @@ const resolveApiKey = (options) => {
|
|
|
13890
14297
|
//#endregion
|
|
13891
14298
|
//#region src/services/telemetry.ts
|
|
13892
14299
|
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
14300
|
const createTelemetryService = (deps) => {
|
|
13910
14301
|
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);
|
|
14302
|
+
const installationIdResult = deps.authStateStore.getInstallationId();
|
|
14303
|
+
const installationId = Result.isOk(installationIdResult) ? installationIdResult.value : randomUUID();
|
|
13919
14304
|
const endpoint = `${deps.baseUrl}${cliEndpoints.telemetry}`;
|
|
13920
14305
|
return { trackCommandCompleted: async (input) => {
|
|
13921
14306
|
if (deps.telemetryDisabled) return;
|
|
@@ -13935,7 +14320,7 @@ const createTelemetryService = (deps) => {
|
|
|
13935
14320
|
if (!payload.success) throw new Error("Invalid CLI telemetry payload");
|
|
13936
14321
|
const apiKeyResolution = resolveApiKey({
|
|
13937
14322
|
apiKeyOverride: deps.getApiKeyOverride?.(),
|
|
13938
|
-
credentialsStore: deps.
|
|
14323
|
+
credentialsStore: deps.authStateStore,
|
|
13939
14324
|
envApiKey: deps.envApiKey
|
|
13940
14325
|
});
|
|
13941
14326
|
const headers = {
|
|
@@ -13966,6 +14351,7 @@ const EXIT_CODE = {
|
|
|
13966
14351
|
rateLimit: 6,
|
|
13967
14352
|
remote: 7,
|
|
13968
14353
|
success: 0,
|
|
14354
|
+
updateRequired: 10,
|
|
13969
14355
|
validation: 2
|
|
13970
14356
|
};
|
|
13971
14357
|
const exitCodeForError = (error) => {
|
|
@@ -13979,6 +14365,7 @@ const exitCodeForError = (error) => {
|
|
|
13979
14365
|
case ERROR_CATEGORY.remote:
|
|
13980
14366
|
case ERROR_CATEGORY.transport:
|
|
13981
14367
|
case ERROR_CATEGORY.authorization: return EXIT_CODE.remote;
|
|
14368
|
+
case ERROR_CATEGORY.updateRequired: return EXIT_CODE.updateRequired;
|
|
13982
14369
|
case ERROR_CATEGORY.contractMismatch: return EXIT_CODE.contractMismatch;
|
|
13983
14370
|
case ERROR_CATEGORY.validation: return EXIT_CODE.validation;
|
|
13984
14371
|
case ERROR_CATEGORY.internal: return EXIT_CODE.internal;
|
|
@@ -14558,6 +14945,9 @@ const ensureSuccess = (result, runtime, context) => {
|
|
|
14558
14945
|
const nextActionsForLogin = (payload, mode) => {
|
|
14559
14946
|
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
14947
|
};
|
|
14948
|
+
const nextActionsForPendingLogin = (userCode, verificationUri) => {
|
|
14949
|
+
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")];
|
|
14950
|
+
};
|
|
14561
14951
|
const nextActionsForAuthStatus = (payload) => {
|
|
14562
14952
|
if (!payload.loggedIn) return [nextAction("login", "Authenticate", cliCommands.auth.login(), "No API key is configured locally; login is required.", "immediate")];
|
|
14563
14953
|
return [nextAction("discover_servers", "Discover servers", cliCommands.catalog.command(), `Authenticated via ${payload.apiKeySource}; discover servers next.`, "after_auth")];
|
|
@@ -14597,42 +14987,35 @@ const runLoginFlow = async (runtime, options) => {
|
|
|
14597
14987
|
commandOptions: options,
|
|
14598
14988
|
invokedCommand: cliCommands.auth.loginWithApiKeyRedacted(),
|
|
14599
14989
|
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
|
-
})();
|
|
14990
|
+
} : {
|
|
14991
|
+
commandOptions: { mode: "device" },
|
|
14992
|
+
invokedCommand: cliCommands.auth.login(),
|
|
14993
|
+
mode: "device"
|
|
14994
|
+
};
|
|
14632
14995
|
const data = ensureSuccess(await runAuthLoginCommand(runtime.context, commandOptions), runtime, {
|
|
14633
14996
|
command: invokedCommand,
|
|
14634
14997
|
entity: { mode }
|
|
14635
14998
|
});
|
|
14999
|
+
if (!data.loggedIn) {
|
|
15000
|
+
const pendingOutput = {
|
|
15001
|
+
event: "auth_login.pending",
|
|
15002
|
+
loggedIn: false,
|
|
15003
|
+
verification: data.verification
|
|
15004
|
+
};
|
|
15005
|
+
runtime.output.success(pendingOutput, {
|
|
15006
|
+
command: invokedCommand,
|
|
15007
|
+
entity: {
|
|
15008
|
+
event: "auth_login.pending",
|
|
15009
|
+
mode,
|
|
15010
|
+
verification: {
|
|
15011
|
+
userCode: data.verification.userCode,
|
|
15012
|
+
verificationUri: data.verification.verificationUri
|
|
15013
|
+
}
|
|
15014
|
+
},
|
|
15015
|
+
nextActions: nextActionsForPendingLogin(data.verification.userCode, data.verification.verificationUri)
|
|
15016
|
+
});
|
|
15017
|
+
return;
|
|
15018
|
+
}
|
|
14636
15019
|
const outputData = {
|
|
14637
15020
|
...data,
|
|
14638
15021
|
event: "auth_login.completed"
|
|
@@ -60381,6 +60764,7 @@ const createCliSentry = (config) => {
|
|
|
60381
60764
|
break;
|
|
60382
60765
|
case "AuthError":
|
|
60383
60766
|
case "ContractMismatchError":
|
|
60767
|
+
case "UpdateRequiredError":
|
|
60384
60768
|
case "UnexpectedError":
|
|
60385
60769
|
case "ValidationError": break;
|
|
60386
60770
|
default: assertNever$1(error);
|
|
@@ -60441,103 +60825,136 @@ const createAccountService = (deps) => {
|
|
|
60441
60825
|
|
|
60442
60826
|
//#endregion
|
|
60443
60827
|
//#region src/services/auth.ts
|
|
60444
|
-
const
|
|
60445
|
-
await new Promise((resolve) => {
|
|
60446
|
-
setTimeout(resolve, milliseconds);
|
|
60447
|
-
});
|
|
60448
|
-
};
|
|
60828
|
+
const DEFAULT_NAMESPACE_KEY = "default";
|
|
60449
60829
|
const toEndpointUrl = (baseUrl, endpoint) => {
|
|
60450
60830
|
return `${baseUrl}${endpoint}`;
|
|
60451
60831
|
};
|
|
60452
|
-
const
|
|
60453
|
-
return toEndpointUrl(baseUrl, cliEndpoints.
|
|
60832
|
+
const authStartUrl = (baseUrl) => {
|
|
60833
|
+
return toEndpointUrl(baseUrl, cliEndpoints.authStart);
|
|
60454
60834
|
};
|
|
60455
|
-
const
|
|
60456
|
-
return toEndpointUrl(baseUrl, cliEndpoints.
|
|
60835
|
+
const authExchangeUrl = (baseUrl) => {
|
|
60836
|
+
return toEndpointUrl(baseUrl, cliEndpoints.authExchange);
|
|
60457
60837
|
};
|
|
60458
|
-
const
|
|
60459
|
-
|
|
60838
|
+
const isExpiredPendingRequest = (request, nowMs) => {
|
|
60839
|
+
const parsedExpiresAt = Date.parse(request.expiresAt);
|
|
60840
|
+
return !Number.isNaN(parsedExpiresAt) && parsedExpiresAt <= nowMs;
|
|
60460
60841
|
};
|
|
60461
60842
|
const createAuthService = (deps) => {
|
|
60462
60843
|
const now = deps.now ?? Date.now;
|
|
60463
|
-
const
|
|
60844
|
+
const storeApprovedAuth = (approvedAuth, namespaceKey = DEFAULT_NAMESPACE_KEY) => {
|
|
60845
|
+
return deps.credentialsStore.storeApprovedAuth({
|
|
60846
|
+
agentName: approvedAuth.agentName,
|
|
60847
|
+
apiKey: approvedAuth.apiKey
|
|
60848
|
+
}, namespaceKey);
|
|
60849
|
+
};
|
|
60850
|
+
const saveApiKeyAuth = (approvedAuth) => {
|
|
60851
|
+
return deps.credentialsStore.save({
|
|
60852
|
+
agentName: approvedAuth.agentName,
|
|
60853
|
+
apiKey: approvedAuth.apiKey
|
|
60854
|
+
});
|
|
60855
|
+
};
|
|
60856
|
+
const tryExchangePendingRequests = async () => {
|
|
60857
|
+
const installationIdResult = deps.credentialsStore.getInstallationId();
|
|
60858
|
+
if (Result.isError(installationIdResult)) return installationIdResult;
|
|
60859
|
+
const pendingRequestsResult = deps.credentialsStore.listPendingRequests();
|
|
60860
|
+
if (Result.isError(pendingRequestsResult)) return pendingRequestsResult;
|
|
60861
|
+
const pendingRequests = [...pendingRequestsResult.value].toSorted((a, b) => {
|
|
60862
|
+
return a.createdAt.localeCompare(b.createdAt);
|
|
60863
|
+
});
|
|
60864
|
+
for (const pendingRequest of pendingRequests) {
|
|
60865
|
+
if (isExpiredPendingRequest(pendingRequest, now())) {
|
|
60866
|
+
const deleteResult = deps.credentialsStore.deletePendingRequest(pendingRequest.authRequestId);
|
|
60867
|
+
if (Result.isError(deleteResult)) return deleteResult;
|
|
60868
|
+
continue;
|
|
60869
|
+
}
|
|
60870
|
+
const exchangePayload = await requestJson(deps.httpClient, authExchangeUrl(deps.baseUrl), authExchangeSchema, {
|
|
60871
|
+
body: JSON.stringify({
|
|
60872
|
+
authRequestId: pendingRequest.authRequestId,
|
|
60873
|
+
installationId: installationIdResult.value,
|
|
60874
|
+
namespaceKey: pendingRequest.namespaceKey
|
|
60875
|
+
}),
|
|
60876
|
+
headers: { "Content-Type": "application/json" },
|
|
60877
|
+
method: "POST"
|
|
60878
|
+
}, {
|
|
60879
|
+
context: "auth exchange response",
|
|
60880
|
+
mapStatusError: (response, body) => new RemoteRequestError({
|
|
60881
|
+
body,
|
|
60882
|
+
httpStatus: response.status,
|
|
60883
|
+
message: "Failed to exchange pending CLI auth",
|
|
60884
|
+
operation: "auth/exchange",
|
|
60885
|
+
raw: body,
|
|
60886
|
+
source: "http"
|
|
60887
|
+
}),
|
|
60888
|
+
operation: "auth/exchange"
|
|
60889
|
+
});
|
|
60890
|
+
if (Result.isError(exchangePayload)) return exchangePayload;
|
|
60891
|
+
const exchangeData = exchangePayload.value.data;
|
|
60892
|
+
if (exchangeData.status === "authorization_pending") continue;
|
|
60893
|
+
if (exchangeData.status === "approved") {
|
|
60894
|
+
const storeResult = storeApprovedAuth({
|
|
60895
|
+
agentName: exchangeData.agentName,
|
|
60896
|
+
apiKey: exchangeData.apiKey
|
|
60897
|
+
}, pendingRequest.namespaceKey);
|
|
60898
|
+
if (Result.isError(storeResult)) return storeResult;
|
|
60899
|
+
return Result.ok({
|
|
60900
|
+
agentName: exchangeData.agentName,
|
|
60901
|
+
apiKey: exchangeData.apiKey
|
|
60902
|
+
});
|
|
60903
|
+
}
|
|
60904
|
+
const deleteResult = deps.credentialsStore.deletePendingRequest(pendingRequest.authRequestId);
|
|
60905
|
+
if (Result.isError(deleteResult)) return deleteResult;
|
|
60906
|
+
}
|
|
60907
|
+
return Result.ok(null);
|
|
60908
|
+
};
|
|
60464
60909
|
const loginWithDevice = async (options) => {
|
|
60465
|
-
const
|
|
60910
|
+
const installationIdResult = deps.credentialsStore.getInstallationId();
|
|
60911
|
+
if (Result.isError(installationIdResult)) return installationIdResult;
|
|
60912
|
+
const startPayload = await requestJson(deps.httpClient, authStartUrl(deps.baseUrl), authStartSchema, {
|
|
60913
|
+
body: JSON.stringify({
|
|
60914
|
+
...deps.executionEnvironment === void 0 ? {} : { executionEnvironment: deps.executionEnvironment },
|
|
60915
|
+
installationId: installationIdResult.value,
|
|
60916
|
+
namespaceKey: DEFAULT_NAMESPACE_KEY
|
|
60917
|
+
}),
|
|
60466
60918
|
headers: { "Content-Type": "application/json" },
|
|
60467
60919
|
method: "POST"
|
|
60468
60920
|
}, {
|
|
60469
|
-
context: "
|
|
60921
|
+
context: "auth start response",
|
|
60470
60922
|
mapStatusError: (response, body) => new RemoteRequestError({
|
|
60471
60923
|
body,
|
|
60472
60924
|
httpStatus: response.status,
|
|
60473
|
-
message: "Failed to start
|
|
60474
|
-
operation: "auth/
|
|
60925
|
+
message: "Failed to start CLI login",
|
|
60926
|
+
operation: "auth/start",
|
|
60475
60927
|
raw: body,
|
|
60476
60928
|
source: "http"
|
|
60477
60929
|
}),
|
|
60478
|
-
operation: "auth/
|
|
60930
|
+
operation: "auth/start"
|
|
60479
60931
|
});
|
|
60480
60932
|
if (Result.isError(startPayload)) return startPayload;
|
|
60481
|
-
|
|
60933
|
+
const savePendingRequestResult = deps.credentialsStore.savePendingRequest({
|
|
60934
|
+
authRequestId: startPayload.value.data.authRequestId,
|
|
60935
|
+
code: startPayload.value.data.userCode,
|
|
60936
|
+
createdAt: new Date(now()).toISOString(),
|
|
60937
|
+
expiresAt: startPayload.value.data.expiresAt,
|
|
60938
|
+
namespaceKey: DEFAULT_NAMESPACE_KEY,
|
|
60939
|
+
verificationUrl: startPayload.value.data.verificationUrl
|
|
60940
|
+
});
|
|
60941
|
+
if (Result.isError(savePendingRequestResult)) return savePendingRequestResult;
|
|
60942
|
+
const verification = {
|
|
60482
60943
|
userCode: startPayload.value.data.userCode,
|
|
60483
|
-
verificationUri: startPayload.value.data.
|
|
60944
|
+
verificationUri: startPayload.value.data.verificationUrl
|
|
60945
|
+
};
|
|
60946
|
+
options.onPending?.(verification);
|
|
60947
|
+
return Result.ok({
|
|
60948
|
+
agentName: null,
|
|
60949
|
+
loggedIn: false,
|
|
60950
|
+
outcome: "pending_approval",
|
|
60951
|
+
verification
|
|
60484
60952
|
});
|
|
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
60953
|
};
|
|
60537
60954
|
return {
|
|
60538
60955
|
login: async (options) => {
|
|
60539
60956
|
if (options.mode === "apiKey" && options.apiKey.length > 0) {
|
|
60540
|
-
const saveResult =
|
|
60957
|
+
const saveResult = saveApiKeyAuth({
|
|
60541
60958
|
agentName: "CLI Agent",
|
|
60542
60959
|
apiKey: options.apiKey
|
|
60543
60960
|
});
|
|
@@ -60560,25 +60977,29 @@ const createAuthService = (deps) => {
|
|
|
60560
60977
|
loggedIn: true,
|
|
60561
60978
|
outcome: "already_authenticated"
|
|
60562
60979
|
});
|
|
60980
|
+
const exchangedPendingAuth = await tryExchangePendingRequests();
|
|
60981
|
+
if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
|
|
60982
|
+
if (exchangedPendingAuth.value !== null) return Result.ok({
|
|
60983
|
+
agentName: exchangedPendingAuth.value.agentName,
|
|
60984
|
+
loggedIn: true,
|
|
60985
|
+
outcome: "authenticated"
|
|
60986
|
+
});
|
|
60563
60987
|
return loginWithDevice(options);
|
|
60564
60988
|
},
|
|
60565
60989
|
logout: async () => {
|
|
60566
60990
|
const stored = deps.credentialsStore.load();
|
|
60567
60991
|
if (Result.isError(stored)) return stored;
|
|
60568
|
-
|
|
60992
|
+
const pendingRequestsResult = deps.credentialsStore.listPendingRequests();
|
|
60993
|
+
if (Result.isError(pendingRequestsResult)) return pendingRequestsResult;
|
|
60994
|
+
if (!(stored.value !== null || pendingRequestsResult.value.length > 0)) return Result.ok({
|
|
60569
60995
|
localCredentialsDeleted: false,
|
|
60570
60996
|
revoked: false
|
|
60571
60997
|
});
|
|
60572
|
-
const revokeResult = await deps.httpClient.request(revokeUrl(deps.baseUrl), {
|
|
60573
|
-
headers: { Authorization: `Bearer ${stored.value.apiKey}` },
|
|
60574
|
-
method: "POST"
|
|
60575
|
-
});
|
|
60576
60998
|
const deleteResult = deps.credentialsStore.delete();
|
|
60577
60999
|
if (Result.isError(deleteResult)) return deleteResult;
|
|
60578
|
-
const revoked = Result.isOk(revokeResult) && revokeResult.value.ok;
|
|
60579
61000
|
return Result.ok({
|
|
60580
61001
|
localCredentialsDeleted: true,
|
|
60581
|
-
revoked
|
|
61002
|
+
revoked: false
|
|
60582
61003
|
});
|
|
60583
61004
|
},
|
|
60584
61005
|
resolveApiKey: async (overrideApiKey) => {
|
|
@@ -60589,6 +61010,9 @@ const createAuthService = (deps) => {
|
|
|
60589
61010
|
});
|
|
60590
61011
|
if (resolution.apiKey !== null) return Result.ok(resolution.apiKey);
|
|
60591
61012
|
if (resolution.source === "credentialsError" && resolution.loadError) return Result.err(resolution.loadError);
|
|
61013
|
+
const exchangedPendingAuth = await tryExchangePendingRequests();
|
|
61014
|
+
if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
|
|
61015
|
+
if (exchangedPendingAuth.value !== null) return Result.ok(exchangedPendingAuth.value.apiKey);
|
|
60592
61016
|
return Result.err(new AuthError({
|
|
60593
61017
|
code: ERROR_CODE.authRequired,
|
|
60594
61018
|
message: "Not logged in. Run `ogment auth login` or set OGMENT_API_KEY.",
|
|
@@ -60602,17 +61026,26 @@ const createAuthService = (deps) => {
|
|
|
60602
61026
|
envApiKey: deps.envApiKey
|
|
60603
61027
|
});
|
|
60604
61028
|
if (resolution.source === "credentialsError" && resolution.loadError) return Result.err(resolution.loadError);
|
|
60605
|
-
if (resolution.apiKey
|
|
61029
|
+
if (resolution.apiKey !== null) {
|
|
61030
|
+
const apiKeySource = resolution.source === "credentialsFile" ? "credentialsFile" : resolution.source === "apiKeyOption" ? "apiKeyOption" : "env";
|
|
61031
|
+
return Result.ok({
|
|
61032
|
+
agentName: resolution.agentName,
|
|
61033
|
+
apiKeySource,
|
|
61034
|
+
loggedIn: true
|
|
61035
|
+
});
|
|
61036
|
+
}
|
|
61037
|
+
const exchangedPendingAuth = await tryExchangePendingRequests();
|
|
61038
|
+
if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
|
|
61039
|
+
if (exchangedPendingAuth.value !== null) return Result.ok({
|
|
61040
|
+
agentName: exchangedPendingAuth.value.agentName,
|
|
61041
|
+
apiKeySource: "credentialsFile",
|
|
61042
|
+
loggedIn: true
|
|
61043
|
+
});
|
|
61044
|
+
return Result.ok({
|
|
60606
61045
|
agentName: null,
|
|
60607
61046
|
apiKeySource: "none",
|
|
60608
61047
|
loggedIn: false
|
|
60609
61048
|
});
|
|
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
61049
|
}
|
|
60617
61050
|
};
|
|
60618
61051
|
};
|
|
@@ -60626,7 +61059,7 @@ const maskApiKey = (apiKey) => {
|
|
|
60626
61059
|
const nextActionByIssueCode = (includeDebug) => {
|
|
60627
61060
|
return {
|
|
60628
61061
|
auth_failed: "Run `ogment auth login` to refresh credentials.",
|
|
60629
|
-
credentials_load_failed: "Check file permissions and contents of `~/.config/ogment/
|
|
61062
|
+
credentials_load_failed: "Check file permissions and contents of `~/.config/ogment/auth-state.json`.",
|
|
60630
61063
|
no_api_key: "Run `ogment auth login` or set `OGMENT_API_KEY`.",
|
|
60631
61064
|
unreachable: includeDebug ? "Verify `OGMENT_BASE_URL` and network connectivity." : "Verify network connectivity."
|
|
60632
61065
|
};
|
|
@@ -60756,11 +61189,11 @@ const createInfoService = (deps) => {
|
|
|
60756
61189
|
});
|
|
60757
61190
|
if (apiKeyResolution.source === "none") issues.push({
|
|
60758
61191
|
code: "no_api_key",
|
|
60759
|
-
message: "No API key found in --api-key, OGMENT_API_KEY, or
|
|
61192
|
+
message: "No API key found in --api-key, OGMENT_API_KEY, or local auth state."
|
|
60760
61193
|
});
|
|
60761
61194
|
if (apiKeyResolution.source === "credentialsError" && apiKeyResolution.loadError !== null) issues.push({
|
|
60762
61195
|
code: "credentials_load_failed",
|
|
60763
|
-
message: `Failed to load
|
|
61196
|
+
message: `Failed to load local auth state: ${apiKeyResolution.loadError.message}`
|
|
60764
61197
|
});
|
|
60765
61198
|
if (account.status === "auth_error") issues.push({
|
|
60766
61199
|
code: "auth_failed",
|
|
@@ -60796,7 +61229,7 @@ const createInfoService = (deps) => {
|
|
|
60796
61229
|
configPrecedence: [
|
|
60797
61230
|
"--api-key option",
|
|
60798
61231
|
"OGMENT_API_KEY environment variable",
|
|
60799
|
-
"~/.config/ogment/
|
|
61232
|
+
"~/.config/ogment/auth-state.json",
|
|
60800
61233
|
...includeDebug ? ["default base URL when OGMENT_BASE_URL is unset"] : []
|
|
60801
61234
|
],
|
|
60802
61235
|
quickCommands: [
|
|
@@ -71591,9 +72024,11 @@ const createRuntime = () => {
|
|
|
71591
72024
|
const runtimeConfig = createRuntimeConfig();
|
|
71592
72025
|
const executionEnvironment = runtimeConfig.executionEnvironment;
|
|
71593
72026
|
const output = new OutputManager();
|
|
71594
|
-
const
|
|
72027
|
+
const authStateStore = createFileCredentialsStore({
|
|
71595
72028
|
configDir: runtimeConfig.configDir,
|
|
71596
|
-
credentialsPath: runtimeConfig.credentialsPath
|
|
72029
|
+
credentialsPath: runtimeConfig.credentialsPath,
|
|
72030
|
+
legacyCredentialsPath: runtimeConfig.legacyCredentialsPath,
|
|
72031
|
+
telemetryPath: runtimeConfig.telemetryPath
|
|
71597
72032
|
});
|
|
71598
72033
|
const httpClient = createHttpClient();
|
|
71599
72034
|
const services = {
|
|
@@ -71603,8 +72038,9 @@ const createRuntime = () => {
|
|
|
71603
72038
|
}),
|
|
71604
72039
|
auth: createAuthService({
|
|
71605
72040
|
baseUrl: runtimeConfig.baseUrl,
|
|
71606
|
-
credentialsStore,
|
|
72041
|
+
credentialsStore: authStateStore,
|
|
71607
72042
|
envApiKey: runtimeConfig.envApiKey,
|
|
72043
|
+
executionEnvironment,
|
|
71608
72044
|
httpClient
|
|
71609
72045
|
}),
|
|
71610
72046
|
mcp: createMcpService({
|
|
@@ -71618,7 +72054,7 @@ const createRuntime = () => {
|
|
|
71618
72054
|
baseUrlSource: runtimeConfig.baseUrlSource,
|
|
71619
72055
|
configDir: runtimeConfig.configDir,
|
|
71620
72056
|
credentialsPath: runtimeConfig.credentialsPath,
|
|
71621
|
-
credentialsStore,
|
|
72057
|
+
credentialsStore: authStateStore,
|
|
71622
72058
|
envApiKey: runtimeConfig.envApiKey,
|
|
71623
72059
|
httpClient,
|
|
71624
72060
|
version: runtimeConfig.version
|
|
@@ -71639,18 +72075,16 @@ const createRuntime = () => {
|
|
|
71639
72075
|
output,
|
|
71640
72076
|
sentry,
|
|
71641
72077
|
telemetry: createTelemetryService({
|
|
72078
|
+
authStateStore,
|
|
71642
72079
|
baseUrl: runtimeConfig.baseUrl,
|
|
71643
72080
|
cliVersion: runtimeConfig.version,
|
|
71644
|
-
configDir: runtimeConfig.configDir,
|
|
71645
|
-
credentialsStore,
|
|
71646
72081
|
envApiKey: runtimeConfig.envApiKey,
|
|
71647
72082
|
executionEnvironment,
|
|
71648
72083
|
getApiKeyOverride: () => {
|
|
71649
72084
|
return context.apiKeyOverride;
|
|
71650
72085
|
},
|
|
71651
72086
|
httpClient,
|
|
71652
|
-
telemetryDisabled: runtimeConfig.telemetryDisabled
|
|
71653
|
-
telemetryPath: runtimeConfig.telemetryPath
|
|
72087
|
+
telemetryDisabled: runtimeConfig.telemetryDisabled
|
|
71654
72088
|
})
|
|
71655
72089
|
};
|
|
71656
72090
|
};
|
|
@@ -71792,6 +72226,7 @@ const commandFromArgv = (argv) => {
|
|
|
71792
72226
|
};
|
|
71793
72227
|
const RUNTIME_ERROR_COMMAND_PATH = "ogment <runtime_error>";
|
|
71794
72228
|
const PARSE_ERROR_COMMAND_PATH = "ogment <parse_error>";
|
|
72229
|
+
const CLI_PACKAGE_NAME = "@ogment-ai/cli";
|
|
71795
72230
|
const PARSE_ERROR_SCOPE_COMMAND_PATHS = {
|
|
71796
72231
|
auth: "ogment auth <parse_error>",
|
|
71797
72232
|
catalog: "ogment catalog <parse_error>",
|
|
@@ -71888,22 +72323,42 @@ const telemetryErrorDetail = (error) => {
|
|
|
71888
72323
|
if (error instanceof Error) return `${error.name}: ${error.message}`;
|
|
71889
72324
|
return "Unknown telemetry error";
|
|
71890
72325
|
};
|
|
72326
|
+
function updateInstallCommand(packageName) {
|
|
72327
|
+
return `npm i -g ${packageName}`;
|
|
72328
|
+
}
|
|
72329
|
+
function formatUpdateRequiredDetail(update) {
|
|
72330
|
+
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(" ");
|
|
72331
|
+
}
|
|
72332
|
+
function createUpdateRequiredError(update) {
|
|
72333
|
+
return new UpdateRequiredError({
|
|
72334
|
+
currentVersion: update.currentVersion,
|
|
72335
|
+
latestVersion: update.latestVersion,
|
|
72336
|
+
message: formatUpdateRequiredDetail(update),
|
|
72337
|
+
packageName: update.packageName,
|
|
72338
|
+
recovery: {
|
|
72339
|
+
command: updateInstallCommand(update.packageName),
|
|
72340
|
+
id: "update_cli",
|
|
72341
|
+
reason: "A newer Ogment CLI version must be installed before this command can run.",
|
|
72342
|
+
title: "Update CLI",
|
|
72343
|
+
when: "immediate"
|
|
72344
|
+
}
|
|
72345
|
+
});
|
|
72346
|
+
}
|
|
71891
72347
|
const emitRuntimeErrorTelemetry = async (output, input) => {
|
|
71892
72348
|
const telemetryConfig = createTelemetryRuntimeConfig();
|
|
71893
|
-
const credentialsStore = createFileCredentialsStore({
|
|
71894
|
-
configDir: telemetryConfig.configDir,
|
|
71895
|
-
credentialsPath: telemetryConfig.credentialsPath
|
|
71896
|
-
});
|
|
71897
72349
|
const telemetry = createTelemetryService({
|
|
72350
|
+
authStateStore: createFileCredentialsStore({
|
|
72351
|
+
configDir: telemetryConfig.configDir,
|
|
72352
|
+
credentialsPath: telemetryConfig.credentialsPath,
|
|
72353
|
+
legacyCredentialsPath: telemetryConfig.legacyCredentialsPath,
|
|
72354
|
+
telemetryPath: telemetryConfig.telemetryPath
|
|
72355
|
+
}),
|
|
71898
72356
|
baseUrl: telemetryConfig.baseUrl,
|
|
71899
72357
|
cliVersion: telemetryConfig.version,
|
|
71900
|
-
configDir: telemetryConfig.configDir,
|
|
71901
|
-
credentialsStore,
|
|
71902
72358
|
envApiKey: telemetryConfig.envApiKey,
|
|
71903
72359
|
executionEnvironment: telemetryConfig.executionEnvironment,
|
|
71904
72360
|
httpClient: createHttpClient(),
|
|
71905
|
-
telemetryDisabled: telemetryConfig.telemetryDisabled
|
|
71906
|
-
telemetryPath: telemetryConfig.telemetryPath
|
|
72361
|
+
telemetryDisabled: telemetryConfig.telemetryDisabled
|
|
71907
72362
|
});
|
|
71908
72363
|
try {
|
|
71909
72364
|
await telemetry.trackCommandCompleted({
|
|
@@ -71954,12 +72409,17 @@ const commandContextFromInvocation = (invocation, apiKeyOverride) => {
|
|
|
71954
72409
|
default: return assertNever(invocation);
|
|
71955
72410
|
}
|
|
71956
72411
|
};
|
|
71957
|
-
const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
|
|
72412
|
+
const runCli = async (argv = process.argv.slice(2), runtime = void 0, deps = {}) => {
|
|
71958
72413
|
const normalizedArgv = normalizeCliArgv(argv);
|
|
71959
72414
|
const outputOptions = parseOutputOptionsFromArgv(normalizedArgv);
|
|
71960
72415
|
const displayCommand = commandFromArgv(argv);
|
|
71961
72416
|
const startedAt = Date.now();
|
|
71962
|
-
const
|
|
72417
|
+
const shouldCheckForUpdate = runtime === void 0 || deps.checkForCliUpdate !== void 0;
|
|
72418
|
+
const checkForCliUpdate = deps.checkForCliUpdate ?? maybeGetAvailableCliUpdate;
|
|
72419
|
+
const updateCheckOptions = {
|
|
72420
|
+
currentVersion: VERSION$2,
|
|
72421
|
+
packageName: CLI_PACKAGE_NAME
|
|
72422
|
+
};
|
|
71963
72423
|
let telemetryContext = null;
|
|
71964
72424
|
let telemetryErrorCode = null;
|
|
71965
72425
|
let shouldEmitTelemetry = true;
|
|
@@ -71991,10 +72451,6 @@ const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
|
|
|
71991
72451
|
executionEnvironment: resolvedRuntime.executionEnvironment
|
|
71992
72452
|
});
|
|
71993
72453
|
};
|
|
71994
|
-
if (runtimeCreatedInternally) await maybeNotifyCliUpdate({
|
|
71995
|
-
currentVersion: VERSION$2,
|
|
71996
|
-
packageName: "@ogment-ai/cli"
|
|
71997
|
-
});
|
|
71998
72454
|
const parseState = createCommanderParseState();
|
|
71999
72455
|
const { program, readInvocation } = createProgram(resolvedRuntime, parseState);
|
|
72000
72456
|
try {
|
|
@@ -72004,12 +72460,23 @@ const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
|
|
|
72004
72460
|
if (invocation !== null) {
|
|
72005
72461
|
resolvedInvocation = invocation;
|
|
72006
72462
|
telemetryContext = telemetryContextFromInvocation(invocation);
|
|
72007
|
-
await
|
|
72463
|
+
const availableUpdate = shouldCheckForUpdate ? await checkForCliUpdate(updateCheckOptions) : null;
|
|
72464
|
+
if (availableUpdate !== null) {
|
|
72465
|
+
const updateError = createUpdateRequiredError(availableUpdate);
|
|
72466
|
+
resolvedRuntime.output.error(updateError, {
|
|
72467
|
+
command: displayCommand,
|
|
72468
|
+
entity: null
|
|
72469
|
+
});
|
|
72470
|
+
telemetryErrorCode = ERROR_CODE.cliUpdateRequired;
|
|
72471
|
+
finalExitCode = exitCodeForError(updateError);
|
|
72472
|
+
} else {
|
|
72473
|
+
await executeInvocation(resolvedRuntime, invocation);
|
|
72474
|
+
finalExitCode = EXIT_CODE.success;
|
|
72475
|
+
}
|
|
72008
72476
|
} else {
|
|
72009
72477
|
shouldEmitTelemetry = false;
|
|
72010
72478
|
resolvedRuntime.output.info("[telemetry] command_completed event not sent: missing telemetry context");
|
|
72011
72479
|
}
|
|
72012
|
-
finalExitCode = EXIT_CODE.success;
|
|
72013
72480
|
} catch (error) {
|
|
72014
72481
|
if (error instanceof CliExitError) {
|
|
72015
72482
|
if (error.error !== void 0) captureCliError(error.error);
|
|
@@ -72089,4 +72556,4 @@ if (shouldExecuteCli(import.meta.url, process.argv[1])) await executeCli();
|
|
|
72089
72556
|
|
|
72090
72557
|
//#endregion
|
|
72091
72558
|
export { executeCli, runCli, shouldExecuteCli };
|
|
72092
|
-
//# debugId=
|
|
72559
|
+
//# debugId=444cb93e-b899-4c52-9527-64988c3dbf06
|