@ogment-ai/cli 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 deleteIfExists = (path, existsSyncFn, unlinkSyncFn) => {
8349
- if (existsSyncFn(path)) unlinkSyncFn(path);
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
- if (parsed.value.apiKey !== void 0) {
8355
- const credentials = { apiKey: parsed.value.apiKey };
8356
- if (typeof parsed.value.agentName === "string") credentials.agentName = parsed.value.agentName;
8357
- return credentials;
8358
- }
8359
- return null;
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 delete credentials"
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
- deleteIfExists(deps.credentialsPath, existsSyncFn, unlinkSyncFn);
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 credentials"
8706
+ message: "Failed to load local auth state"
8385
8707
  }),
8386
8708
  try: () => {
8387
- if (!existsSyncFn(deps.credentialsPath)) return null;
8388
- const raw = readFileSyncFn(deps.credentialsPath, "utf8");
8389
- const normalized = normalizeCredentials(JSON.parse(raw));
8390
- if (normalized === null) deleteIfExists(deps.credentialsPath, existsSyncFn, unlinkSyncFn);
8391
- return normalized;
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 save credentials"
8722
+ message: "Failed to update local auth state"
8400
8723
  }),
8401
8724
  try: () => {
8402
- mkdirSyncFn(deps.configDir, { recursive: true });
8403
- if (existsSyncFn(deps.credentialsPath) && lstatSyncFn(deps.credentialsPath).isSymbolicLink()) unlinkSyncFn(deps.credentialsPath);
8404
- writeFileSyncFn(deps.credentialsPath, JSON.stringify(credentials, null, 2), { mode: 384 });
8725
+ withLock(() => {
8726
+ writeState({
8727
+ ...loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState(),
8728
+ activeAuth: {
8729
+ ...credentials.agentName === void 0 ? {} : { agentName: credentials.agentName },
8730
+ apiKey: credentials.apiKey
8731
+ }
8732
+ });
8733
+ });
8734
+ }
8735
+ });
8736
+ },
8737
+ storeApprovedAuth: (credentials, namespaceKey) => {
8738
+ return Result.try({
8739
+ catch: (cause) => new UnexpectedError({
8740
+ cause,
8741
+ message: "Failed to update local auth state"
8742
+ }),
8743
+ try: () => {
8744
+ withLock(() => {
8745
+ const currentState = loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState();
8746
+ writeState({
8747
+ ...currentState,
8748
+ activeAuth: {
8749
+ ...credentials.agentName === void 0 ? {} : { agentName: credentials.agentName },
8750
+ apiKey: credentials.apiKey,
8751
+ boundAt: nowFn().toISOString(),
8752
+ ...namespaceKey === void 0 ? {} : { namespaceKey }
8753
+ },
8754
+ pendingRequests: namespaceKey === void 0 ? [] : currentState.pendingRequests.filter((request) => request.namespaceKey !== namespaceKey)
8755
+ });
8756
+ });
8757
+ }
8758
+ });
8759
+ },
8760
+ savePendingRequest: (request) => {
8761
+ return Result.try({
8762
+ catch: (cause) => new UnexpectedError({
8763
+ cause,
8764
+ message: "Failed to update local auth state"
8765
+ }),
8766
+ try: () => {
8767
+ withLock(() => {
8768
+ const currentState = loadStateFromDisk() ?? migrateStateFromLegacy() ?? createEmptyState();
8769
+ const pendingRequests = currentState.pendingRequests.filter((entry) => entry.authRequestId !== request.authRequestId);
8770
+ pendingRequests.push({
8771
+ authRequestId: request.authRequestId,
8772
+ code: request.code,
8773
+ createdAt: request.createdAt,
8774
+ expiresAt: request.expiresAt,
8775
+ namespaceKey: request.namespaceKey,
8776
+ ...request.scopeFingerprint === void 0 ? {} : { scopeFingerprint: request.scopeFingerprint },
8777
+ verificationUrl: request.verificationUrl
8778
+ });
8779
+ writeState({
8780
+ ...currentState,
8781
+ pendingRequests
8782
+ });
8783
+ });
8405
8784
  }
8406
8785
  });
8407
8786
  }
@@ -8421,7 +8800,7 @@ const DEFAULT_OGMENT_BASE_URL = "https://dashboard.ogment.ai";
8421
8800
  */
8422
8801
  const SENTRY_DSN_BUILD = "https://4219ab98670b61086758b4d0e31ae318@o4507724844957696.ingest.us.sentry.io/4510992932405248";
8423
8802
  const SENTRY_ENVIRONMENT_BUILD = "production";
8424
- const SENTRY_RELEASE_BUILD = "cli-v0.8.2+git:61c0ca10c";
8803
+ const SENTRY_RELEASE_BUILD = "cli-v0.9.0+git:f88c23e58";
8425
8804
  const packageJsonSchema = object({ version: string().min(1) });
8426
8805
  const hasCode = (value, code) => {
8427
8806
  if (typeof value !== "object" || value === null) return false;
@@ -8477,9 +8856,10 @@ const resolveSentryConfig = (env) => {
8477
8856
  const telemetryRuntimeFields = (env, configDir) => {
8478
8857
  return {
8479
8858
  configDir,
8480
- credentialsPath: join(configDir, "credentials.json"),
8859
+ credentialsPath: join(configDir, "auth-state.json"),
8481
8860
  envApiKey: env["OGMENT_API_KEY"],
8482
8861
  executionEnvironment: detectExecutionEnvironment(env),
8862
+ legacyCredentialsPath: join(configDir, "credentials.json"),
8483
8863
  telemetryDisabled: telemetryDisabledFromEnv(env),
8484
8864
  telemetryPath: join(configDir, "telemetry.json"),
8485
8865
  version: VERSION$2
@@ -13351,6 +13731,8 @@ const UPDATE_CHECK_CACHE_FILENAME = "update-check.json";
13351
13731
  const UPDATE_CHECK_INTERVAL_MS = 3600 * 1e3;
13352
13732
  const LOOKUP_TIMEOUT_MS = 1500;
13353
13733
  const PACKAGE_VERSION_SELECTOR = "latest";
13734
+ const OGMENT_NO_UPDATE_CHECK_ENV_VAR = "OGMENT_NO_UPDATE_CHECK";
13735
+ const NO_UPDATE_NOTIFIER_ENV_VAR = "NO_UPDATE_NOTIFIER";
13354
13736
  const cacheSchema = object({
13355
13737
  lastCheckedAtMs: number$1().int().nonnegative(),
13356
13738
  latestVersion: string().optional()
@@ -13361,8 +13743,8 @@ const isTruthyEnvValue = (value) => {
13361
13743
  return normalized !== "" && normalized !== "0" && normalized !== "false";
13362
13744
  };
13363
13745
  const shouldSkipUpdateCheck = (env) => {
13364
- if (isTruthyEnvValue(env["OGMENT_NO_UPDATE_CHECK"])) return true;
13365
- if (isTruthyEnvValue(env["NO_UPDATE_NOTIFIER"])) return true;
13746
+ if (isTruthyEnvValue(env[OGMENT_NO_UPDATE_CHECK_ENV_VAR])) return true;
13747
+ if (isTruthyEnvValue(env[NO_UPDATE_NOTIFIER_ENV_VAR])) return true;
13366
13748
  if (isTruthyEnvValue(env["CI"])) return true;
13367
13749
  return false;
13368
13750
  };
@@ -13406,33 +13788,17 @@ const isNewerVersion = (latestVersion, currentVersion) => {
13406
13788
  if (latest === null || current === null) return false;
13407
13789
  return import_semver.default.gt(latest, current);
13408
13790
  };
13409
- const toUpdateMessage = (currentVersion, latestVersion, packageName) => {
13410
- return [
13411
- "",
13412
- "A newer Ogment CLI is available.",
13413
- "",
13414
- `Current: v${currentVersion}`,
13415
- `Latest: v${latestVersion}`,
13416
- "",
13417
- "Recommended update:",
13418
- `npm i -g ${packageName}`,
13419
- "",
13420
- "Continuing with your current version for this run.",
13421
- ""
13422
- ].join("\n");
13423
- };
13424
13791
  const defaultConfigDir = () => {
13425
13792
  return join(homedir(), ".config", "ogment");
13426
13793
  };
13427
13794
  const cachePathFromConfigDir = (configDir) => {
13428
13795
  return join(configDir, UPDATE_CHECK_CACHE_FILENAME);
13429
13796
  };
13430
- const maybeNotifyCliUpdate = async (options, deps = {}) => {
13797
+ const maybeGetAvailableCliUpdate = async (options, deps = {}) => {
13431
13798
  try {
13432
- if (shouldSkipUpdateCheck(options.env ?? process.env)) return;
13799
+ if (shouldSkipUpdateCheck(options.env ?? process.env)) return null;
13433
13800
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
13434
13801
  const fetchLatestVersion = deps.fetchLatestVersion ?? defaultFetchLatestVersion;
13435
- const stderr = options.stderr ?? process.stderr;
13436
13802
  const cachePath = cachePathFromConfigDir(options.configDir ?? defaultConfigDir());
13437
13803
  let latestVersion;
13438
13804
  const existingCache = readCache(cachePath);
@@ -13448,8 +13814,13 @@ const maybeNotifyCliUpdate = async (options, deps = {}) => {
13448
13814
  lastCheckedAtMs: nowMs
13449
13815
  });
13450
13816
  } else latestVersion = existingCache.latestVersion;
13451
- if (latestVersion !== void 0 && isNewerVersion(latestVersion, options.currentVersion)) stderr.write(toUpdateMessage(options.currentVersion, latestVersion, options.packageName));
13817
+ if (latestVersion !== void 0 && isNewerVersion(latestVersion, options.currentVersion)) return {
13818
+ currentVersion: options.currentVersion,
13819
+ latestVersion,
13820
+ packageName: options.packageName
13821
+ };
13452
13822
  } catch {}
13823
+ return null;
13453
13824
  };
13454
13825
 
13455
13826
  //#endregion
@@ -13789,6 +14160,13 @@ const deviceCodeStartDataSchema = object({
13789
14160
  verificationUri: string().url()
13790
14161
  }).strict();
13791
14162
  const deviceCodeStartSchema = cliSuccessEnvelopeSchema(deviceCodeStartDataSchema);
14163
+ const authStartDataSchema = object({
14164
+ authRequestId: string().min(1),
14165
+ expiresAt: string().datetime(),
14166
+ userCode: string().min(1),
14167
+ verificationUrl: string().url()
14168
+ }).strict();
14169
+ const authStartSchema = cliSuccessEnvelopeSchema(authStartDataSchema);
13792
14170
  const deviceTokenPendingDataSchema = object({ status: literal("authorization_pending") }).strict();
13793
14171
  const deviceTokenApprovedDataSchema = object({
13794
14172
  agentName: string().min(1),
@@ -13798,6 +14176,27 @@ const deviceTokenApprovedDataSchema = object({
13798
14176
  const deviceTokenDataSchema = discriminatedUnion("status", [deviceTokenPendingDataSchema, deviceTokenApprovedDataSchema]);
13799
14177
  const deviceTokenSchema = cliSuccessEnvelopeSchema(deviceTokenDataSchema);
13800
14178
  const deviceTokenApprovedSchema = cliSuccessEnvelopeSchema(deviceTokenApprovedDataSchema);
14179
+ const authExchangePendingDataSchema = object({ status: literal("authorization_pending") }).strict();
14180
+ const authExchangeApprovedDataSchema = object({
14181
+ agentName: string().min(1),
14182
+ apiKey: string().min(1),
14183
+ scopeFingerprint: string().min(1),
14184
+ status: literal("approved")
14185
+ }).strict();
14186
+ const authExchangeExpiredDataSchema = object({ status: literal("expired") }).strict();
14187
+ const authExchangeDeniedDataSchema = object({ status: literal("denied") }).strict();
14188
+ const authExchangeConflictDataSchema = object({ status: literal("conflict") }).strict();
14189
+ const authExchangeInvalidDataSchema = object({ status: literal("invalid") }).strict();
14190
+ const authExchangeDataSchema = discriminatedUnion("status", [
14191
+ authExchangePendingDataSchema,
14192
+ authExchangeApprovedDataSchema,
14193
+ authExchangeExpiredDataSchema,
14194
+ authExchangeDeniedDataSchema,
14195
+ authExchangeConflictDataSchema,
14196
+ authExchangeInvalidDataSchema
14197
+ ]);
14198
+ const authExchangeSchema = cliSuccessEnvelopeSchema(authExchangeDataSchema);
14199
+ const authExchangeApprovedSchema = cliSuccessEnvelopeSchema(authExchangeApprovedDataSchema);
13801
14200
  const revokeSelfDataSchema = object({ success: literal(true) }).strict();
13802
14201
  const revokeSelfSchema = cliSuccessEnvelopeSchema(revokeSelfDataSchema);
13803
14202
 
@@ -13805,6 +14204,8 @@ const revokeSelfSchema = cliSuccessEnvelopeSchema(revokeSelfDataSchema);
13805
14204
  //#region ../../packages/shared/src/cli/endpoints.ts
13806
14205
  const cliEndpoints = {
13807
14206
  accountMe: "/api/v1/cli/me",
14207
+ authExchange: "/api/v1/cli/auth/exchange",
14208
+ authStart: "/api/v1/cli/auth/start",
13808
14209
  cliRevokeSelf: "/api/v1/cli/revoke-self",
13809
14210
  deviceCode: "/api/v1/cli/device-code",
13810
14211
  deviceToken: "/api/v1/cli/device-token",
@@ -13890,32 +14291,10 @@ const resolveApiKey = (options) => {
13890
14291
  //#endregion
13891
14292
  //#region src/services/telemetry.ts
13892
14293
  const TELEMETRY_TIMEOUT_MS = 500;
13893
- const telemetryInstallationSchema = object({ installationId: uuid() }).strict();
13894
- const loadOrCreateInstallationId = (telemetryPath, configDir, fileDeps) => {
13895
- try {
13896
- if (fileDeps.existsSyncFn(telemetryPath)) {
13897
- const raw = fileDeps.readFileSyncFn(telemetryPath, "utf8");
13898
- const parsed = telemetryInstallationSchema.safeParse(JSON.parse(raw));
13899
- if (parsed.success) return parsed.data.installationId;
13900
- }
13901
- } catch {}
13902
- const installationId = fileDeps.randomUuidFn();
13903
- try {
13904
- fileDeps.mkdirSyncFn(configDir, { recursive: true });
13905
- fileDeps.writeFileSyncFn(telemetryPath, JSON.stringify({ installationId }, null, 2), { mode: 384 });
13906
- } catch {}
13907
- return installationId;
13908
- };
13909
14294
  const createTelemetryService = (deps) => {
13910
14295
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
13911
- const fileDeps = {
13912
- existsSyncFn: existsSync,
13913
- mkdirSyncFn: mkdirSync,
13914
- randomUuidFn: randomUUID,
13915
- readFileSyncFn: readFileSync,
13916
- writeFileSyncFn: writeFileSync
13917
- };
13918
- const installationId = loadOrCreateInstallationId(deps.telemetryPath, deps.configDir, fileDeps);
14296
+ const installationIdResult = deps.authStateStore.getInstallationId();
14297
+ const installationId = Result.isOk(installationIdResult) ? installationIdResult.value : randomUUID();
13919
14298
  const endpoint = `${deps.baseUrl}${cliEndpoints.telemetry}`;
13920
14299
  return { trackCommandCompleted: async (input) => {
13921
14300
  if (deps.telemetryDisabled) return;
@@ -13935,7 +14314,7 @@ const createTelemetryService = (deps) => {
13935
14314
  if (!payload.success) throw new Error("Invalid CLI telemetry payload");
13936
14315
  const apiKeyResolution = resolveApiKey({
13937
14316
  apiKeyOverride: deps.getApiKeyOverride?.(),
13938
- credentialsStore: deps.credentialsStore,
14317
+ credentialsStore: deps.authStateStore,
13939
14318
  envApiKey: deps.envApiKey
13940
14319
  });
13941
14320
  const headers = {
@@ -13966,6 +14345,7 @@ const EXIT_CODE = {
13966
14345
  rateLimit: 6,
13967
14346
  remote: 7,
13968
14347
  success: 0,
14348
+ updateRequired: 10,
13969
14349
  validation: 2
13970
14350
  };
13971
14351
  const exitCodeForError = (error) => {
@@ -13979,6 +14359,7 @@ const exitCodeForError = (error) => {
13979
14359
  case ERROR_CATEGORY.remote:
13980
14360
  case ERROR_CATEGORY.transport:
13981
14361
  case ERROR_CATEGORY.authorization: return EXIT_CODE.remote;
14362
+ case ERROR_CATEGORY.updateRequired: return EXIT_CODE.updateRequired;
13982
14363
  case ERROR_CATEGORY.contractMismatch: return EXIT_CODE.contractMismatch;
13983
14364
  case ERROR_CATEGORY.validation: return EXIT_CODE.validation;
13984
14365
  case ERROR_CATEGORY.internal: return EXIT_CODE.internal;
@@ -14558,6 +14939,9 @@ const ensureSuccess = (result, runtime, context) => {
14558
14939
  const nextActionsForLogin = (payload, mode) => {
14559
14940
  return [nextAction("check_auth_status", "Check auth status", cliCommands.auth.status(), `Verify persisted credentials after ${mode} login for ${payload.agentName}.`, "immediate"), nextAction("discover_servers", "Discover servers", cliCommands.catalog.command(), `Logged in as ${payload.agentName} via ${mode}; discover available servers.`, "after_auth")];
14560
14941
  };
14942
+ const nextActionsForPendingLogin = (userCode, verificationUri) => {
14943
+ return [nextAction("check_login_status", "Check login status", cliCommands.auth.status(), `Approve ${userCode} at ${verificationUri}, then verify local auth state.`, "immediate"), nextAction("restart_device_login", "Restart login", cliCommands.auth.login(), `Restart login if code ${userCode} expires before approval.`, "if_expired")];
14944
+ };
14561
14945
  const nextActionsForAuthStatus = (payload) => {
14562
14946
  if (!payload.loggedIn) return [nextAction("login", "Authenticate", cliCommands.auth.login(), "No API key is configured locally; login is required.", "immediate")];
14563
14947
  return [nextAction("discover_servers", "Discover servers", cliCommands.catalog.command(), `Authenticated via ${payload.apiKeySource}; discover servers next.`, "after_auth")];
@@ -14597,42 +14981,35 @@ const runLoginFlow = async (runtime, options) => {
14597
14981
  commandOptions: options,
14598
14982
  invokedCommand: cliCommands.auth.loginWithApiKeyRedacted(),
14599
14983
  mode: "apiKey"
14600
- } : (() => {
14601
- const deviceCommand = cliCommands.auth.login();
14602
- return {
14603
- commandOptions: {
14604
- mode: "device",
14605
- onPending: ({ userCode, verificationUri }) => {
14606
- const pendingNextActions = [nextAction("check_login_status", "Check login status", cliCommands.auth.status(), `Approve ${userCode} at ${verificationUri}, then verify local auth state.`, "immediate"), nextAction("restart_device_login", "Restart login", cliCommands.auth.login(), `Restart login if code ${userCode} expires before approval.`, "if_expired")];
14607
- runtime.output.success({
14608
- event: "auth_login.pending",
14609
- loggedIn: false,
14610
- verification: {
14611
- userCode,
14612
- verificationUri
14613
- }
14614
- }, {
14615
- command: deviceCommand,
14616
- entity: {
14617
- event: "auth_login.pending",
14618
- mode: "device",
14619
- verification: {
14620
- userCode,
14621
- verificationUri
14622
- }
14623
- },
14624
- nextActions: pendingNextActions
14625
- });
14626
- }
14627
- },
14628
- invokedCommand: deviceCommand,
14629
- mode: "device"
14630
- };
14631
- })();
14984
+ } : {
14985
+ commandOptions: { mode: "device" },
14986
+ invokedCommand: cliCommands.auth.login(),
14987
+ mode: "device"
14988
+ };
14632
14989
  const data = ensureSuccess(await runAuthLoginCommand(runtime.context, commandOptions), runtime, {
14633
14990
  command: invokedCommand,
14634
14991
  entity: { mode }
14635
14992
  });
14993
+ if (!data.loggedIn) {
14994
+ const pendingOutput = {
14995
+ event: "auth_login.pending",
14996
+ loggedIn: false,
14997
+ verification: data.verification
14998
+ };
14999
+ runtime.output.success(pendingOutput, {
15000
+ command: invokedCommand,
15001
+ entity: {
15002
+ event: "auth_login.pending",
15003
+ mode,
15004
+ verification: {
15005
+ userCode: data.verification.userCode,
15006
+ verificationUri: data.verification.verificationUri
15007
+ }
15008
+ },
15009
+ nextActions: nextActionsForPendingLogin(data.verification.userCode, data.verification.verificationUri)
15010
+ });
15011
+ return;
15012
+ }
14636
15013
  const outputData = {
14637
15014
  ...data,
14638
15015
  event: "auth_login.completed"
@@ -60381,6 +60758,7 @@ const createCliSentry = (config) => {
60381
60758
  break;
60382
60759
  case "AuthError":
60383
60760
  case "ContractMismatchError":
60761
+ case "UpdateRequiredError":
60384
60762
  case "UnexpectedError":
60385
60763
  case "ValidationError": break;
60386
60764
  default: assertNever$1(error);
@@ -60441,103 +60819,136 @@ const createAccountService = (deps) => {
60441
60819
 
60442
60820
  //#endregion
60443
60821
  //#region src/services/auth.ts
60444
- const defaultSleep = async (milliseconds) => {
60445
- await new Promise((resolve) => {
60446
- setTimeout(resolve, milliseconds);
60447
- });
60448
- };
60822
+ const DEFAULT_NAMESPACE_KEY = "default";
60449
60823
  const toEndpointUrl = (baseUrl, endpoint) => {
60450
60824
  return `${baseUrl}${endpoint}`;
60451
60825
  };
60452
- const deviceCodeUrl = (baseUrl) => {
60453
- return toEndpointUrl(baseUrl, cliEndpoints.deviceCode);
60826
+ const authStartUrl = (baseUrl) => {
60827
+ return toEndpointUrl(baseUrl, cliEndpoints.authStart);
60454
60828
  };
60455
- const deviceTokenUrl = (baseUrl) => {
60456
- return toEndpointUrl(baseUrl, cliEndpoints.deviceToken);
60829
+ const authExchangeUrl = (baseUrl) => {
60830
+ return toEndpointUrl(baseUrl, cliEndpoints.authExchange);
60457
60831
  };
60458
- const revokeUrl = (baseUrl) => {
60459
- return toEndpointUrl(baseUrl, cliEndpoints.cliRevokeSelf);
60832
+ const isExpiredPendingRequest = (request, nowMs) => {
60833
+ const parsedExpiresAt = Date.parse(request.expiresAt);
60834
+ return !Number.isNaN(parsedExpiresAt) && parsedExpiresAt <= nowMs;
60460
60835
  };
60461
60836
  const createAuthService = (deps) => {
60462
60837
  const now = deps.now ?? Date.now;
60463
- const sleep = deps.sleep ?? defaultSleep;
60838
+ const storeApprovedAuth = (approvedAuth, namespaceKey = DEFAULT_NAMESPACE_KEY) => {
60839
+ return deps.credentialsStore.storeApprovedAuth({
60840
+ agentName: approvedAuth.agentName,
60841
+ apiKey: approvedAuth.apiKey
60842
+ }, namespaceKey);
60843
+ };
60844
+ const saveApiKeyAuth = (approvedAuth) => {
60845
+ return deps.credentialsStore.save({
60846
+ agentName: approvedAuth.agentName,
60847
+ apiKey: approvedAuth.apiKey
60848
+ });
60849
+ };
60850
+ const tryExchangePendingRequests = async () => {
60851
+ const installationIdResult = deps.credentialsStore.getInstallationId();
60852
+ if (Result.isError(installationIdResult)) return installationIdResult;
60853
+ const pendingRequestsResult = deps.credentialsStore.listPendingRequests();
60854
+ if (Result.isError(pendingRequestsResult)) return pendingRequestsResult;
60855
+ const pendingRequests = [...pendingRequestsResult.value].toSorted((a, b) => {
60856
+ return a.createdAt.localeCompare(b.createdAt);
60857
+ });
60858
+ for (const pendingRequest of pendingRequests) {
60859
+ if (isExpiredPendingRequest(pendingRequest, now())) {
60860
+ const deleteResult = deps.credentialsStore.deletePendingRequest(pendingRequest.authRequestId);
60861
+ if (Result.isError(deleteResult)) return deleteResult;
60862
+ continue;
60863
+ }
60864
+ const exchangePayload = await requestJson(deps.httpClient, authExchangeUrl(deps.baseUrl), authExchangeSchema, {
60865
+ body: JSON.stringify({
60866
+ authRequestId: pendingRequest.authRequestId,
60867
+ installationId: installationIdResult.value,
60868
+ namespaceKey: pendingRequest.namespaceKey
60869
+ }),
60870
+ headers: { "Content-Type": "application/json" },
60871
+ method: "POST"
60872
+ }, {
60873
+ context: "auth exchange response",
60874
+ mapStatusError: (response, body) => new RemoteRequestError({
60875
+ body,
60876
+ httpStatus: response.status,
60877
+ message: "Failed to exchange pending CLI auth",
60878
+ operation: "auth/exchange",
60879
+ raw: body,
60880
+ source: "http"
60881
+ }),
60882
+ operation: "auth/exchange"
60883
+ });
60884
+ if (Result.isError(exchangePayload)) return exchangePayload;
60885
+ const exchangeData = exchangePayload.value.data;
60886
+ if (exchangeData.status === "authorization_pending") continue;
60887
+ if (exchangeData.status === "approved") {
60888
+ const storeResult = storeApprovedAuth({
60889
+ agentName: exchangeData.agentName,
60890
+ apiKey: exchangeData.apiKey
60891
+ }, pendingRequest.namespaceKey);
60892
+ if (Result.isError(storeResult)) return storeResult;
60893
+ return Result.ok({
60894
+ agentName: exchangeData.agentName,
60895
+ apiKey: exchangeData.apiKey
60896
+ });
60897
+ }
60898
+ const deleteResult = deps.credentialsStore.deletePendingRequest(pendingRequest.authRequestId);
60899
+ if (Result.isError(deleteResult)) return deleteResult;
60900
+ }
60901
+ return Result.ok(null);
60902
+ };
60464
60903
  const loginWithDevice = async (options) => {
60465
- const startPayload = await requestJson(deps.httpClient, deviceCodeUrl(deps.baseUrl), deviceCodeStartSchema, {
60904
+ const installationIdResult = deps.credentialsStore.getInstallationId();
60905
+ if (Result.isError(installationIdResult)) return installationIdResult;
60906
+ const startPayload = await requestJson(deps.httpClient, authStartUrl(deps.baseUrl), authStartSchema, {
60907
+ body: JSON.stringify({
60908
+ ...deps.executionEnvironment === void 0 ? {} : { executionEnvironment: deps.executionEnvironment },
60909
+ installationId: installationIdResult.value,
60910
+ namespaceKey: DEFAULT_NAMESPACE_KEY
60911
+ }),
60466
60912
  headers: { "Content-Type": "application/json" },
60467
60913
  method: "POST"
60468
60914
  }, {
60469
- context: "device login start response",
60915
+ context: "auth start response",
60470
60916
  mapStatusError: (response, body) => new RemoteRequestError({
60471
60917
  body,
60472
60918
  httpStatus: response.status,
60473
- message: "Failed to start device login",
60474
- operation: "auth/device/start",
60919
+ message: "Failed to start CLI login",
60920
+ operation: "auth/start",
60475
60921
  raw: body,
60476
60922
  source: "http"
60477
60923
  }),
60478
- operation: "auth/device/start"
60924
+ operation: "auth/start"
60479
60925
  });
60480
60926
  if (Result.isError(startPayload)) return startPayload;
60481
- options.onPending?.({
60927
+ const savePendingRequestResult = deps.credentialsStore.savePendingRequest({
60928
+ authRequestId: startPayload.value.data.authRequestId,
60929
+ code: startPayload.value.data.userCode,
60930
+ createdAt: new Date(now()).toISOString(),
60931
+ expiresAt: startPayload.value.data.expiresAt,
60932
+ namespaceKey: DEFAULT_NAMESPACE_KEY,
60933
+ verificationUrl: startPayload.value.data.verificationUrl
60934
+ });
60935
+ if (Result.isError(savePendingRequestResult)) return savePendingRequestResult;
60936
+ const verification = {
60482
60937
  userCode: startPayload.value.data.userCode,
60483
- verificationUri: startPayload.value.data.verificationUri
60938
+ verificationUri: startPayload.value.data.verificationUrl
60939
+ };
60940
+ options.onPending?.(verification);
60941
+ return Result.ok({
60942
+ agentName: null,
60943
+ loggedIn: false,
60944
+ outcome: "pending_approval",
60945
+ verification
60484
60946
  });
60485
- const deadline = now() + startPayload.value.data.expiresIn * 1e3;
60486
- const interval = startPayload.value.data.interval * 1e3;
60487
- while (now() < deadline) {
60488
- await sleep(interval);
60489
- const pollPayload = await requestJson(deps.httpClient, deviceTokenUrl(deps.baseUrl), deviceTokenSchema, {
60490
- body: JSON.stringify({ deviceCode: startPayload.value.data.deviceCode }),
60491
- headers: { "Content-Type": "application/json" },
60492
- method: "POST"
60493
- }, {
60494
- context: "device login poll response",
60495
- mapStatusError: (response, body) => {
60496
- if (response.status === 410) return new AuthError({
60497
- code: ERROR_CODE.authDeviceExpired,
60498
- message: "Device login code expired. Run `ogment auth login` again.",
60499
- recovery: { command: "ogment auth login" }
60500
- });
60501
- if (response.status === 404) return new AuthError({
60502
- code: ERROR_CODE.authInvalidCredentials,
60503
- message: "Invalid device login code. Run `ogment auth login` again.",
60504
- recovery: { command: "ogment auth login" }
60505
- });
60506
- return new RemoteRequestError({
60507
- body,
60508
- httpStatus: response.status,
60509
- message: "Failed to poll device login status",
60510
- operation: "auth/device/poll",
60511
- raw: body,
60512
- source: "http"
60513
- });
60514
- },
60515
- operation: "auth/device/poll"
60516
- });
60517
- if (Result.isError(pollPayload)) return pollPayload;
60518
- if (pollPayload.value.data.status === "authorization_pending") continue;
60519
- const approvedPayload = pollPayload.value.data;
60520
- const saveResult = deps.credentialsStore.save({
60521
- agentName: approvedPayload.agentName,
60522
- apiKey: approvedPayload.apiKey
60523
- });
60524
- if (Result.isError(saveResult)) return saveResult;
60525
- return Result.ok({
60526
- agentName: approvedPayload.agentName,
60527
- loggedIn: true,
60528
- outcome: "authenticated"
60529
- });
60530
- }
60531
- return Result.err(new AuthError({
60532
- code: ERROR_CODE.authDeviceExpired,
60533
- message: "Device login code expired. Run `ogment auth login` again.",
60534
- recovery: { command: "ogment auth login" }
60535
- }));
60536
60947
  };
60537
60948
  return {
60538
60949
  login: async (options) => {
60539
60950
  if (options.mode === "apiKey" && options.apiKey.length > 0) {
60540
- const saveResult = deps.credentialsStore.save({
60951
+ const saveResult = saveApiKeyAuth({
60541
60952
  agentName: "CLI Agent",
60542
60953
  apiKey: options.apiKey
60543
60954
  });
@@ -60560,25 +60971,29 @@ const createAuthService = (deps) => {
60560
60971
  loggedIn: true,
60561
60972
  outcome: "already_authenticated"
60562
60973
  });
60974
+ const exchangedPendingAuth = await tryExchangePendingRequests();
60975
+ if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
60976
+ if (exchangedPendingAuth.value !== null) return Result.ok({
60977
+ agentName: exchangedPendingAuth.value.agentName,
60978
+ loggedIn: true,
60979
+ outcome: "authenticated"
60980
+ });
60563
60981
  return loginWithDevice(options);
60564
60982
  },
60565
60983
  logout: async () => {
60566
60984
  const stored = deps.credentialsStore.load();
60567
60985
  if (Result.isError(stored)) return stored;
60568
- if (stored.value === null) return Result.ok({
60986
+ const pendingRequestsResult = deps.credentialsStore.listPendingRequests();
60987
+ if (Result.isError(pendingRequestsResult)) return pendingRequestsResult;
60988
+ if (!(stored.value !== null || pendingRequestsResult.value.length > 0)) return Result.ok({
60569
60989
  localCredentialsDeleted: false,
60570
60990
  revoked: false
60571
60991
  });
60572
- const revokeResult = await deps.httpClient.request(revokeUrl(deps.baseUrl), {
60573
- headers: { Authorization: `Bearer ${stored.value.apiKey}` },
60574
- method: "POST"
60575
- });
60576
60992
  const deleteResult = deps.credentialsStore.delete();
60577
60993
  if (Result.isError(deleteResult)) return deleteResult;
60578
- const revoked = Result.isOk(revokeResult) && revokeResult.value.ok;
60579
60994
  return Result.ok({
60580
60995
  localCredentialsDeleted: true,
60581
- revoked
60996
+ revoked: false
60582
60997
  });
60583
60998
  },
60584
60999
  resolveApiKey: async (overrideApiKey) => {
@@ -60589,6 +61004,9 @@ const createAuthService = (deps) => {
60589
61004
  });
60590
61005
  if (resolution.apiKey !== null) return Result.ok(resolution.apiKey);
60591
61006
  if (resolution.source === "credentialsError" && resolution.loadError) return Result.err(resolution.loadError);
61007
+ const exchangedPendingAuth = await tryExchangePendingRequests();
61008
+ if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
61009
+ if (exchangedPendingAuth.value !== null) return Result.ok(exchangedPendingAuth.value.apiKey);
60592
61010
  return Result.err(new AuthError({
60593
61011
  code: ERROR_CODE.authRequired,
60594
61012
  message: "Not logged in. Run `ogment auth login` or set OGMENT_API_KEY.",
@@ -60602,17 +61020,26 @@ const createAuthService = (deps) => {
60602
61020
  envApiKey: deps.envApiKey
60603
61021
  });
60604
61022
  if (resolution.source === "credentialsError" && resolution.loadError) return Result.err(resolution.loadError);
60605
- if (resolution.apiKey === null) return Result.ok({
61023
+ if (resolution.apiKey !== null) {
61024
+ const apiKeySource = resolution.source === "credentialsFile" ? "credentialsFile" : resolution.source === "apiKeyOption" ? "apiKeyOption" : "env";
61025
+ return Result.ok({
61026
+ agentName: resolution.agentName,
61027
+ apiKeySource,
61028
+ loggedIn: true
61029
+ });
61030
+ }
61031
+ const exchangedPendingAuth = await tryExchangePendingRequests();
61032
+ if (Result.isError(exchangedPendingAuth)) return exchangedPendingAuth;
61033
+ if (exchangedPendingAuth.value !== null) return Result.ok({
61034
+ agentName: exchangedPendingAuth.value.agentName,
61035
+ apiKeySource: "credentialsFile",
61036
+ loggedIn: true
61037
+ });
61038
+ return Result.ok({
60606
61039
  agentName: null,
60607
61040
  apiKeySource: "none",
60608
61041
  loggedIn: false
60609
61042
  });
60610
- const apiKeySource = resolution.source === "credentialsFile" ? "credentialsFile" : resolution.source === "apiKeyOption" ? "apiKeyOption" : "env";
60611
- return Result.ok({
60612
- agentName: resolution.agentName,
60613
- apiKeySource,
60614
- loggedIn: true
60615
- });
60616
61043
  }
60617
61044
  };
60618
61045
  };
@@ -60626,7 +61053,7 @@ const maskApiKey = (apiKey) => {
60626
61053
  const nextActionByIssueCode = (includeDebug) => {
60627
61054
  return {
60628
61055
  auth_failed: "Run `ogment auth login` to refresh credentials.",
60629
- credentials_load_failed: "Check file permissions and contents of `~/.config/ogment/credentials.json`.",
61056
+ credentials_load_failed: "Check file permissions and contents of `~/.config/ogment/auth-state.json`.",
60630
61057
  no_api_key: "Run `ogment auth login` or set `OGMENT_API_KEY`.",
60631
61058
  unreachable: includeDebug ? "Verify `OGMENT_BASE_URL` and network connectivity." : "Verify network connectivity."
60632
61059
  };
@@ -60756,11 +61183,11 @@ const createInfoService = (deps) => {
60756
61183
  });
60757
61184
  if (apiKeyResolution.source === "none") issues.push({
60758
61185
  code: "no_api_key",
60759
- message: "No API key found in --api-key, OGMENT_API_KEY, or credentials file."
61186
+ message: "No API key found in --api-key, OGMENT_API_KEY, or local auth state."
60760
61187
  });
60761
61188
  if (apiKeyResolution.source === "credentialsError" && apiKeyResolution.loadError !== null) issues.push({
60762
61189
  code: "credentials_load_failed",
60763
- message: `Failed to load credentials file: ${apiKeyResolution.loadError.message}`
61190
+ message: `Failed to load local auth state: ${apiKeyResolution.loadError.message}`
60764
61191
  });
60765
61192
  if (account.status === "auth_error") issues.push({
60766
61193
  code: "auth_failed",
@@ -60796,7 +61223,7 @@ const createInfoService = (deps) => {
60796
61223
  configPrecedence: [
60797
61224
  "--api-key option",
60798
61225
  "OGMENT_API_KEY environment variable",
60799
- "~/.config/ogment/credentials.json",
61226
+ "~/.config/ogment/auth-state.json",
60800
61227
  ...includeDebug ? ["default base URL when OGMENT_BASE_URL is unset"] : []
60801
61228
  ],
60802
61229
  quickCommands: [
@@ -71591,9 +72018,11 @@ const createRuntime = () => {
71591
72018
  const runtimeConfig = createRuntimeConfig();
71592
72019
  const executionEnvironment = runtimeConfig.executionEnvironment;
71593
72020
  const output = new OutputManager();
71594
- const credentialsStore = createFileCredentialsStore({
72021
+ const authStateStore = createFileCredentialsStore({
71595
72022
  configDir: runtimeConfig.configDir,
71596
- credentialsPath: runtimeConfig.credentialsPath
72023
+ credentialsPath: runtimeConfig.credentialsPath,
72024
+ legacyCredentialsPath: runtimeConfig.legacyCredentialsPath,
72025
+ telemetryPath: runtimeConfig.telemetryPath
71597
72026
  });
71598
72027
  const httpClient = createHttpClient();
71599
72028
  const services = {
@@ -71603,8 +72032,9 @@ const createRuntime = () => {
71603
72032
  }),
71604
72033
  auth: createAuthService({
71605
72034
  baseUrl: runtimeConfig.baseUrl,
71606
- credentialsStore,
72035
+ credentialsStore: authStateStore,
71607
72036
  envApiKey: runtimeConfig.envApiKey,
72037
+ executionEnvironment,
71608
72038
  httpClient
71609
72039
  }),
71610
72040
  mcp: createMcpService({
@@ -71618,7 +72048,7 @@ const createRuntime = () => {
71618
72048
  baseUrlSource: runtimeConfig.baseUrlSource,
71619
72049
  configDir: runtimeConfig.configDir,
71620
72050
  credentialsPath: runtimeConfig.credentialsPath,
71621
- credentialsStore,
72051
+ credentialsStore: authStateStore,
71622
72052
  envApiKey: runtimeConfig.envApiKey,
71623
72053
  httpClient,
71624
72054
  version: runtimeConfig.version
@@ -71639,18 +72069,16 @@ const createRuntime = () => {
71639
72069
  output,
71640
72070
  sentry,
71641
72071
  telemetry: createTelemetryService({
72072
+ authStateStore,
71642
72073
  baseUrl: runtimeConfig.baseUrl,
71643
72074
  cliVersion: runtimeConfig.version,
71644
- configDir: runtimeConfig.configDir,
71645
- credentialsStore,
71646
72075
  envApiKey: runtimeConfig.envApiKey,
71647
72076
  executionEnvironment,
71648
72077
  getApiKeyOverride: () => {
71649
72078
  return context.apiKeyOverride;
71650
72079
  },
71651
72080
  httpClient,
71652
- telemetryDisabled: runtimeConfig.telemetryDisabled,
71653
- telemetryPath: runtimeConfig.telemetryPath
72081
+ telemetryDisabled: runtimeConfig.telemetryDisabled
71654
72082
  })
71655
72083
  };
71656
72084
  };
@@ -71792,6 +72220,7 @@ const commandFromArgv = (argv) => {
71792
72220
  };
71793
72221
  const RUNTIME_ERROR_COMMAND_PATH = "ogment <runtime_error>";
71794
72222
  const PARSE_ERROR_COMMAND_PATH = "ogment <parse_error>";
72223
+ const CLI_PACKAGE_NAME = "@ogment-ai/cli";
71795
72224
  const PARSE_ERROR_SCOPE_COMMAND_PATHS = {
71796
72225
  auth: "ogment auth <parse_error>",
71797
72226
  catalog: "ogment catalog <parse_error>",
@@ -71888,22 +72317,42 @@ const telemetryErrorDetail = (error) => {
71888
72317
  if (error instanceof Error) return `${error.name}: ${error.message}`;
71889
72318
  return "Unknown telemetry error";
71890
72319
  };
72320
+ function updateInstallCommand(packageName) {
72321
+ return `npm i -g ${packageName}`;
72322
+ }
72323
+ function formatUpdateRequiredDetail(update) {
72324
+ return [`Update the Ogment CLI before retrying this command (current: v${update.currentVersion}; latest: v${update.latestVersion}).`, `Run \`${updateInstallCommand(update.packageName)}\` to update, or set \`${OGMENT_NO_UPDATE_CHECK_ENV_VAR}=1\` or \`${NO_UPDATE_NOTIFIER_ENV_VAR}=1\` to bypass this check.`].join(" ");
72325
+ }
72326
+ function createUpdateRequiredError(update) {
72327
+ return new UpdateRequiredError({
72328
+ currentVersion: update.currentVersion,
72329
+ latestVersion: update.latestVersion,
72330
+ message: formatUpdateRequiredDetail(update),
72331
+ packageName: update.packageName,
72332
+ recovery: {
72333
+ command: updateInstallCommand(update.packageName),
72334
+ id: "update_cli",
72335
+ reason: "A newer Ogment CLI version must be installed before this command can run.",
72336
+ title: "Update CLI",
72337
+ when: "immediate"
72338
+ }
72339
+ });
72340
+ }
71891
72341
  const emitRuntimeErrorTelemetry = async (output, input) => {
71892
72342
  const telemetryConfig = createTelemetryRuntimeConfig();
71893
- const credentialsStore = createFileCredentialsStore({
71894
- configDir: telemetryConfig.configDir,
71895
- credentialsPath: telemetryConfig.credentialsPath
71896
- });
71897
72343
  const telemetry = createTelemetryService({
72344
+ authStateStore: createFileCredentialsStore({
72345
+ configDir: telemetryConfig.configDir,
72346
+ credentialsPath: telemetryConfig.credentialsPath,
72347
+ legacyCredentialsPath: telemetryConfig.legacyCredentialsPath,
72348
+ telemetryPath: telemetryConfig.telemetryPath
72349
+ }),
71898
72350
  baseUrl: telemetryConfig.baseUrl,
71899
72351
  cliVersion: telemetryConfig.version,
71900
- configDir: telemetryConfig.configDir,
71901
- credentialsStore,
71902
72352
  envApiKey: telemetryConfig.envApiKey,
71903
72353
  executionEnvironment: telemetryConfig.executionEnvironment,
71904
72354
  httpClient: createHttpClient(),
71905
- telemetryDisabled: telemetryConfig.telemetryDisabled,
71906
- telemetryPath: telemetryConfig.telemetryPath
72355
+ telemetryDisabled: telemetryConfig.telemetryDisabled
71907
72356
  });
71908
72357
  try {
71909
72358
  await telemetry.trackCommandCompleted({
@@ -71954,12 +72403,17 @@ const commandContextFromInvocation = (invocation, apiKeyOverride) => {
71954
72403
  default: return assertNever(invocation);
71955
72404
  }
71956
72405
  };
71957
- const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
72406
+ const runCli = async (argv = process.argv.slice(2), runtime = void 0, deps = {}) => {
71958
72407
  const normalizedArgv = normalizeCliArgv(argv);
71959
72408
  const outputOptions = parseOutputOptionsFromArgv(normalizedArgv);
71960
72409
  const displayCommand = commandFromArgv(argv);
71961
72410
  const startedAt = Date.now();
71962
- const runtimeCreatedInternally = runtime === void 0;
72411
+ const shouldCheckForUpdate = runtime === void 0 || deps.checkForCliUpdate !== void 0;
72412
+ const checkForCliUpdate = deps.checkForCliUpdate ?? maybeGetAvailableCliUpdate;
72413
+ const updateCheckOptions = {
72414
+ currentVersion: VERSION$2,
72415
+ packageName: CLI_PACKAGE_NAME
72416
+ };
71963
72417
  let telemetryContext = null;
71964
72418
  let telemetryErrorCode = null;
71965
72419
  let shouldEmitTelemetry = true;
@@ -71991,10 +72445,6 @@ const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
71991
72445
  executionEnvironment: resolvedRuntime.executionEnvironment
71992
72446
  });
71993
72447
  };
71994
- if (runtimeCreatedInternally) await maybeNotifyCliUpdate({
71995
- currentVersion: VERSION$2,
71996
- packageName: "@ogment-ai/cli"
71997
- });
71998
72448
  const parseState = createCommanderParseState();
71999
72449
  const { program, readInvocation } = createProgram(resolvedRuntime, parseState);
72000
72450
  try {
@@ -72004,12 +72454,23 @@ const runCli = async (argv = process.argv.slice(2), runtime = void 0) => {
72004
72454
  if (invocation !== null) {
72005
72455
  resolvedInvocation = invocation;
72006
72456
  telemetryContext = telemetryContextFromInvocation(invocation);
72007
- await executeInvocation(resolvedRuntime, invocation);
72457
+ const availableUpdate = shouldCheckForUpdate ? await checkForCliUpdate(updateCheckOptions) : null;
72458
+ if (availableUpdate !== null) {
72459
+ const updateError = createUpdateRequiredError(availableUpdate);
72460
+ resolvedRuntime.output.error(updateError, {
72461
+ command: displayCommand,
72462
+ entity: null
72463
+ });
72464
+ telemetryErrorCode = ERROR_CODE.cliUpdateRequired;
72465
+ finalExitCode = exitCodeForError(updateError);
72466
+ } else {
72467
+ await executeInvocation(resolvedRuntime, invocation);
72468
+ finalExitCode = EXIT_CODE.success;
72469
+ }
72008
72470
  } else {
72009
72471
  shouldEmitTelemetry = false;
72010
72472
  resolvedRuntime.output.info("[telemetry] command_completed event not sent: missing telemetry context");
72011
72473
  }
72012
- finalExitCode = EXIT_CODE.success;
72013
72474
  } catch (error) {
72014
72475
  if (error instanceof CliExitError) {
72015
72476
  if (error.error !== void 0) captureCliError(error.error);
@@ -72089,4 +72550,4 @@ if (shouldExecuteCli(import.meta.url, process.argv[1])) await executeCli();
72089
72550
 
72090
72551
  //#endregion
72091
72552
  export { executeCli, runCli, shouldExecuteCli };
72092
- //# debugId=3ea61f88-7f34-4abe-adff-ad066ac07739
72553
+ //# debugId=856f5502-c592-445c-b0d7-d8c7048e63ba