@oxygen-agent/cli 1.99.1 → 1.109.10
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 +1 -1
- package/dist/http-client.d.ts +4 -0
- package/dist/http-client.js +96 -10
- package/dist/index.js +14 -6
- package/node_modules/@oxygen/shared/dist/index.d.ts +4 -3
- package/node_modules/@oxygen/shared/dist/index.js +8 -6
- package/node_modules/@oxygen/shared/dist/version.d.ts +2 -1
- package/node_modules/@oxygen/shared/dist/version.js +3 -1
- package/node_modules/@oxygen/workflows/dist/index.d.ts +3 -3
- package/node_modules/@oxygen/workflows/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/http-client.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ type RequestOptions = {
|
|
|
5
5
|
formData?: FormData;
|
|
6
6
|
credentials?: StoredCredentials;
|
|
7
7
|
requireAuth?: boolean;
|
|
8
|
+
enforceMinimumCliVersion?: boolean;
|
|
8
9
|
timeoutMs?: number;
|
|
9
10
|
traceId?: string;
|
|
10
11
|
fetch?: typeof fetch;
|
|
@@ -12,4 +13,7 @@ type RequestOptions = {
|
|
|
12
13
|
};
|
|
13
14
|
export declare function requestOxygen<T>(// skipcq: JS-R1005
|
|
14
15
|
path: string, options?: RequestOptions): Promise<T>;
|
|
16
|
+
export declare function ensureFreshCliForApiUrl(apiUrl: string, options?: {
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
}): Promise<void>;
|
|
15
19
|
export {};
|
package/dist/http-client.js
CHANGED
|
@@ -2,6 +2,9 @@ import { OXYGEN_VERSION, OxygenError } from "@oxygen/shared";
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { defaultApiUrl, loadCredentials } from "./credentials.js";
|
|
4
4
|
const DEFAULT_REQUEST_TIMEOUT_MS = 120_000;
|
|
5
|
+
const CLI_COMPATIBILITY_CHECK_TIMEOUT_MS = 5_000;
|
|
6
|
+
const PROD_API_HOSTNAME = "oxygen-agent.com";
|
|
7
|
+
const cliCompatibilityCheckKeys = new Set();
|
|
5
8
|
export async function requestOxygen(// skipcq: JS-R1005
|
|
6
9
|
path, options = {}) {
|
|
7
10
|
const credentials = options.credentials
|
|
@@ -12,6 +15,10 @@ path, options = {}) {
|
|
|
12
15
|
});
|
|
13
16
|
}
|
|
14
17
|
const apiUrl = credentials?.apiUrl ?? defaultApiUrl();
|
|
18
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
19
|
+
if (path !== "/api/health" && shouldCheckCliCompatibility(options)) {
|
|
20
|
+
await ensureFreshCliForApiUrl(apiUrl, { fetch: fetchImpl });
|
|
21
|
+
}
|
|
15
22
|
const traceId = options.traceId ?? randomUUID();
|
|
16
23
|
const headers = {
|
|
17
24
|
Accept: "application/json",
|
|
@@ -34,7 +41,6 @@ path, options = {}) {
|
|
|
34
41
|
let response;
|
|
35
42
|
const timeoutMs = resolveRequestTimeoutMs(options.timeoutMs);
|
|
36
43
|
const timeout = createTimeoutSignal(timeoutMs);
|
|
37
|
-
const fetchImpl = options.fetch ?? fetch;
|
|
38
44
|
const requestInit = {
|
|
39
45
|
method: options.method ?? "GET",
|
|
40
46
|
headers,
|
|
@@ -70,18 +76,31 @@ path, options = {}) {
|
|
|
70
76
|
timeout.cancel();
|
|
71
77
|
}
|
|
72
78
|
const envelope = await readEnvelope(response);
|
|
73
|
-
const
|
|
74
|
-
|
|
79
|
+
const compatibility = readEnvelopeCompatibility(envelope);
|
|
80
|
+
assertCliMeetsMinimumApiVersion(compatibility, options);
|
|
81
|
+
warnIfCliIsOlderThanApi(compatibility.version);
|
|
75
82
|
if (!response.ok || !envelope.ok) {
|
|
76
83
|
const failure = envelope;
|
|
77
84
|
const responseTraceId = response.headers.get("x-oxygen-trace-id") ?? traceId;
|
|
78
85
|
throw new OxygenError(failure.error.code, failure.error.message, {
|
|
79
|
-
details: withTraceDetails(failure.error.details, responseTraceId,
|
|
86
|
+
details: withTraceDetails(failure.error.details, responseTraceId, compatibility),
|
|
80
87
|
exitCode: response.status >= 500 ? 1 : 1,
|
|
81
88
|
});
|
|
82
89
|
}
|
|
83
90
|
return envelope.data;
|
|
84
91
|
}
|
|
92
|
+
export async function ensureFreshCliForApiUrl(apiUrl, options = {}) {
|
|
93
|
+
const checkKey = `${apiUrl}|${OXYGEN_VERSION}`;
|
|
94
|
+
if (cliCompatibilityCheckKeys.has(checkKey))
|
|
95
|
+
return;
|
|
96
|
+
const compatibility = await readHealthCompatibility(apiUrl, options.fetch ?? fetch);
|
|
97
|
+
if (!compatibility) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
assertCliMeetsMinimumApiVersion(compatibility, {});
|
|
101
|
+
warnIfCliIsOlderThanApi(compatibility.version);
|
|
102
|
+
cliCompatibilityCheckKeys.add(checkKey);
|
|
103
|
+
}
|
|
85
104
|
function resolveSelectedOrganization(credentials, explicit) {
|
|
86
105
|
const fromOption = explicit?.trim();
|
|
87
106
|
if (fromOption)
|
|
@@ -93,6 +112,38 @@ function resolveSelectedOrganization(credentials, explicit) {
|
|
|
93
112
|
?? credentials?.activeOrganization?.id
|
|
94
113
|
?? null;
|
|
95
114
|
}
|
|
115
|
+
function shouldCheckCliCompatibility(options) {
|
|
116
|
+
if (options.enforceMinimumCliVersion === false)
|
|
117
|
+
return false;
|
|
118
|
+
// Tests and local callers often inject a fake fetch implementation for a
|
|
119
|
+
// single request. Skip the prod health preflight there; the response envelope
|
|
120
|
+
// still carries the compatibility metadata and is checked below.
|
|
121
|
+
if (options.fetch)
|
|
122
|
+
return false;
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
async function readHealthCompatibility(apiUrl, fetchImpl) {
|
|
126
|
+
if (!isProdApiUrl(apiUrl))
|
|
127
|
+
return null;
|
|
128
|
+
const timeout = createTimeoutSignal(CLI_COMPATIBILITY_CHECK_TIMEOUT_MS);
|
|
129
|
+
try {
|
|
130
|
+
const requestInit = {
|
|
131
|
+
method: "GET",
|
|
132
|
+
headers: { Accept: "application/json" },
|
|
133
|
+
};
|
|
134
|
+
if (timeout.signal)
|
|
135
|
+
requestInit.signal = timeout.signal;
|
|
136
|
+
const response = await fetchImpl(new URL("/api/health", apiUrl), requestInit);
|
|
137
|
+
const envelope = await readEnvelope(response);
|
|
138
|
+
return readEnvelopeCompatibility(envelope);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
timeout.cancel();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
96
147
|
const staleCliWarningVersions = new Set();
|
|
97
148
|
function warnIfCliIsOlderThanApi(serverVersion) {
|
|
98
149
|
if (!serverVersion || staleCliWarningVersions.has(serverVersion))
|
|
@@ -103,21 +154,56 @@ function warnIfCliIsOlderThanApi(serverVersion) {
|
|
|
103
154
|
process.stderr.write(`[oxygen] CLI version ${OXYGEN_VERSION} is older than Oxygen API version ${serverVersion}. `
|
|
104
155
|
+ "Run `oxygen update` before using operational commands.\n");
|
|
105
156
|
}
|
|
106
|
-
function
|
|
157
|
+
function isProdApiUrl(apiUrl) {
|
|
158
|
+
try {
|
|
159
|
+
return new URL(apiUrl).hostname === PROD_API_HOSTNAME;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function assertCliMeetsMinimumApiVersion(compatibility, options) {
|
|
166
|
+
if (options.enforceMinimumCliVersion === false)
|
|
167
|
+
return;
|
|
168
|
+
const minimumCliVersion = compatibility.minimumCliVersion;
|
|
169
|
+
if (!minimumCliVersion || !isVersionGreater(minimumCliVersion, OXYGEN_VERSION))
|
|
170
|
+
return;
|
|
171
|
+
throw new OxygenError("cli_update_required", "This Oxygen API requires a newer CLI. Run `oxygen update` before using this command.", {
|
|
172
|
+
details: {
|
|
173
|
+
client_version: OXYGEN_VERSION,
|
|
174
|
+
...(compatibility.version ? { server_version: compatibility.version } : {}),
|
|
175
|
+
minimum_cli_version: minimumCliVersion,
|
|
176
|
+
cli_update_command: "oxygen update",
|
|
177
|
+
},
|
|
178
|
+
exitCode: 1,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function readEnvelopeCompatibility(envelope) {
|
|
107
182
|
const meta = envelope.meta;
|
|
108
183
|
if (!meta || typeof meta !== "object" || Array.isArray(meta))
|
|
109
|
-
return
|
|
110
|
-
const
|
|
111
|
-
|
|
184
|
+
return {};
|
|
185
|
+
const record = meta;
|
|
186
|
+
const compatibility = {};
|
|
187
|
+
if (typeof record.version === "string")
|
|
188
|
+
compatibility.version = record.version;
|
|
189
|
+
if (typeof record.minimum_cli_version === "string") {
|
|
190
|
+
compatibility.minimumCliVersion = record.minimum_cli_version;
|
|
191
|
+
}
|
|
192
|
+
return compatibility;
|
|
112
193
|
}
|
|
113
|
-
function withTraceDetails(details, traceId,
|
|
194
|
+
function withTraceDetails(details, traceId, compatibility) {
|
|
195
|
+
const serverVersion = compatibility.version;
|
|
114
196
|
const fields = {
|
|
115
197
|
trace_id: traceId,
|
|
116
198
|
client_version: OXYGEN_VERSION,
|
|
117
199
|
};
|
|
118
200
|
if (serverVersion)
|
|
119
201
|
fields.server_version = serverVersion;
|
|
120
|
-
if (
|
|
202
|
+
if (compatibility.minimumCliVersion)
|
|
203
|
+
fields.minimum_cli_version = compatibility.minimumCliVersion;
|
|
204
|
+
if ((serverVersion && isVersionGreater(serverVersion, OXYGEN_VERSION))
|
|
205
|
+
|| (compatibility.minimumCliVersion
|
|
206
|
+
&& isVersionGreater(compatibility.minimumCliVersion, OXYGEN_VERSION))) {
|
|
121
207
|
fields.cli_update_command = "oxygen update";
|
|
122
208
|
}
|
|
123
209
|
if (details && typeof details === "object" && !Array.isArray(details)) {
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { assertRecipeBundleSafe, assertWorkflowManifest, buildRecipeManifest, co
|
|
|
15
15
|
import { isRecipeDefinition } from "@oxygen/recipe-sdk";
|
|
16
16
|
import { createBrowserLoginSession, openBrowser } from "./browser-login.js";
|
|
17
17
|
import { clearCredentials, defaultApiUrl, listCredentialProfiles, normalizeApiUrl, pickProfileNameForIdentity, pickProfileNameForUserSession, resolveActiveProfile, saveCredentials, switchCredentialProfile, updateActiveOrganizationForProfile, } from "./credentials.js";
|
|
18
|
-
import { requestOxygen } from "./http-client.js";
|
|
18
|
+
import { ensureFreshCliForApiUrl, requestOxygen } from "./http-client.js";
|
|
19
19
|
import { runLocalCustomHttpColumn } from "./local-custom-http-column.js";
|
|
20
20
|
import { addSessionOutput, addSessionStatus, getSessionUsage, startSession, updateSessionStep, } from "./session.js";
|
|
21
21
|
import { doctorAgentSkills, installAgentSkills, listAgentSkills, runAutomaticSkillsInstall, } from "./skills.js";
|
|
@@ -325,10 +325,11 @@ export function createProgram() {
|
|
|
325
325
|
.option("--json", "Print a JSON envelope.")
|
|
326
326
|
.action(async (options) => {
|
|
327
327
|
await handleAsyncAction("status", options, async () => {
|
|
328
|
-
const server = await requestOxygen("/api/health", { requireAuth: false });
|
|
328
|
+
const server = await requestOxygen("/api/health", { requireAuth: false, enforceMinimumCliVersion: false });
|
|
329
329
|
return {
|
|
330
330
|
client_version: OXYGEN_VERSION,
|
|
331
331
|
server_version: server.server_version,
|
|
332
|
+
minimum_cli_version: server.minimum_cli_version ?? null,
|
|
332
333
|
sha: server.sha,
|
|
333
334
|
region: server.region,
|
|
334
335
|
in_sync: server.server_version === OXYGEN_VERSION,
|
|
@@ -1348,7 +1349,7 @@ export function createProgram() {
|
|
|
1348
1349
|
.option("--connection-id <connection_id>", "Optional provider integration connection id.")
|
|
1349
1350
|
.option("--background", "Create a durable background table action run instead of executing synchronously.")
|
|
1350
1351
|
.option("--max-credits <n>", "Maximum managed/provider credits to reserve for a background run.")
|
|
1351
|
-
.option("--max-concurrency <n>", "Maximum concurrent row items for a background run. Defaults to 50.")
|
|
1352
|
+
.option("--max-concurrency <n>", "Maximum concurrent row items for a background run. Defaults to 250 for AI columns and 50 otherwise.")
|
|
1352
1353
|
.option("--local", "Run a custom HTTP column in this CLI process so env-var secrets stay local.")
|
|
1353
1354
|
.option("--local-concurrency <n>", "Maximum concurrent custom HTTP requests for --local. Defaults to 3.")
|
|
1354
1355
|
.option("--json", "Print a JSON envelope.")
|
|
@@ -4389,7 +4390,9 @@ async function resolveActiveProfileWithSource() {
|
|
|
4389
4390
|
}
|
|
4390
4391
|
async function handleWhoamiAction(options) {
|
|
4391
4392
|
try {
|
|
4392
|
-
const identity = await requestOxygen("/api/cli/whoami"
|
|
4393
|
+
const identity = await requestOxygen("/api/cli/whoami", {
|
|
4394
|
+
enforceMinimumCliVersion: false,
|
|
4395
|
+
});
|
|
4393
4396
|
const context = await resolveActiveProfileWithSource();
|
|
4394
4397
|
if (context.resolution.exists) {
|
|
4395
4398
|
const existingCredentials = context.resolution.credentials;
|
|
@@ -4777,6 +4780,7 @@ function resolveApiKeyExpiresAt(options) {
|
|
|
4777
4780
|
}
|
|
4778
4781
|
async function login(options) {
|
|
4779
4782
|
const apiUrl = normalizeApiUrl(readOption(options.apiUrl) ?? defaultApiUrl());
|
|
4783
|
+
await ensureFreshCliForApiUrl(apiUrl);
|
|
4780
4784
|
const token = readOption(options.token) ?? await promptForToken({
|
|
4781
4785
|
apiUrl,
|
|
4782
4786
|
browser: options.browser !== false,
|
|
@@ -4821,9 +4825,11 @@ async function applyAuthToken(options) {
|
|
|
4821
4825
|
exitCode: 1,
|
|
4822
4826
|
});
|
|
4823
4827
|
}
|
|
4828
|
+
const apiUrl = normalizeApiUrl(readOption(options.apiUrl) ?? defaultApiUrl());
|
|
4829
|
+
await ensureFreshCliForApiUrl(apiUrl);
|
|
4824
4830
|
const credentials = {
|
|
4825
4831
|
token,
|
|
4826
|
-
apiUrl
|
|
4832
|
+
apiUrl,
|
|
4827
4833
|
};
|
|
4828
4834
|
const loginIdentity = await resolveLoginIdentity(token, credentials.apiUrl);
|
|
4829
4835
|
const explicitProfile = readOption(options.profile) ?? readEnvProfileName();
|
|
@@ -4875,7 +4881,9 @@ async function resolveLoginIdentity(token, apiUrl) {
|
|
|
4875
4881
|
};
|
|
4876
4882
|
if (token.startsWith("oxy_live_"))
|
|
4877
4883
|
credentials.authKind = "org_api_key";
|
|
4878
|
-
const identity = await requestOxygen("/api/cli/whoami", {
|
|
4884
|
+
const identity = await requestOxygen("/api/cli/whoami", {
|
|
4885
|
+
credentials,
|
|
4886
|
+
});
|
|
4879
4887
|
const activeOrganization = storedOrganizationFromOption(identity.organization);
|
|
4880
4888
|
credentials.authKind = identity.authType === "user_session" ? "user_session" : "org_api_key";
|
|
4881
4889
|
credentials.activeOrganization = activeOrganization;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { OXYGEN_VERSION } from "./version.js";
|
|
1
|
+
export { OXYGEN_MINIMUM_CLI_VERSION, OXYGEN_VERSION } from "./version.js";
|
|
2
2
|
export * from "./billing.js";
|
|
3
3
|
export * from "./cell-format.js";
|
|
4
4
|
export * from "./column-types.js";
|
|
@@ -12,6 +12,7 @@ export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
|
12
12
|
export type CliMeta = {
|
|
13
13
|
command: string;
|
|
14
14
|
version: string;
|
|
15
|
+
minimum_cli_version?: string;
|
|
15
16
|
};
|
|
16
17
|
export type CliSuccess<T> = {
|
|
17
18
|
ok: true;
|
|
@@ -37,10 +38,10 @@ export declare class OxygenError extends Error {
|
|
|
37
38
|
exitCode?: number;
|
|
38
39
|
});
|
|
39
40
|
}
|
|
40
|
-
export declare function success<T>(command: string, data: T, version?: string): CliSuccess<T>;
|
|
41
|
+
export declare function success<T>(command: string, data: T, version?: string, minimumCliVersion?: string): CliSuccess<T>;
|
|
41
42
|
export declare function failure(command: string, error: {
|
|
42
43
|
code: string;
|
|
43
44
|
message: string;
|
|
44
45
|
details?: unknown;
|
|
45
|
-
}, version?: string): CliFailure;
|
|
46
|
+
}, version?: string, minimumCliVersion?: string): CliFailure;
|
|
46
47
|
export declare function toFailure(command: string, error: unknown, version?: string): CliFailure;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { OXYGEN_VERSION } from "./version.js";
|
|
2
|
-
export { OXYGEN_VERSION } from "./version.js";
|
|
1
|
+
import { OXYGEN_MINIMUM_CLI_VERSION, OXYGEN_VERSION } from "./version.js";
|
|
2
|
+
export { OXYGEN_MINIMUM_CLI_VERSION, OXYGEN_VERSION } from "./version.js";
|
|
3
3
|
export * from "./billing.js";
|
|
4
4
|
export * from "./cell-format.js";
|
|
5
5
|
export * from "./column-types.js";
|
|
@@ -19,23 +19,25 @@ export class OxygenError extends Error {
|
|
|
19
19
|
this.exitCode = options.exitCode ?? 1;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
export function success(command, data, version = OXYGEN_VERSION) {
|
|
22
|
+
export function success(command, data, version = OXYGEN_VERSION, minimumCliVersion = OXYGEN_MINIMUM_CLI_VERSION) {
|
|
23
23
|
return {
|
|
24
24
|
ok: true,
|
|
25
25
|
data,
|
|
26
26
|
meta: {
|
|
27
27
|
command,
|
|
28
|
-
version
|
|
28
|
+
version,
|
|
29
|
+
minimum_cli_version: minimumCliVersion,
|
|
29
30
|
}
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
|
-
export function failure(command, error, version = OXYGEN_VERSION) {
|
|
33
|
+
export function failure(command, error, version = OXYGEN_VERSION, minimumCliVersion = OXYGEN_MINIMUM_CLI_VERSION) {
|
|
33
34
|
return {
|
|
34
35
|
ok: false,
|
|
35
36
|
error,
|
|
36
37
|
meta: {
|
|
37
38
|
command,
|
|
38
|
-
version
|
|
39
|
+
version,
|
|
40
|
+
minimum_cli_version: minimumCliVersion,
|
|
39
41
|
}
|
|
40
42
|
};
|
|
41
43
|
}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export declare const OXYGEN_VERSION = "1.
|
|
1
|
+
export declare const OXYGEN_VERSION = "1.109.10";
|
|
2
|
+
export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.0.0";
|
|
@@ -414,7 +414,7 @@ export declare const workflowTriggerSchema: {
|
|
|
414
414
|
readonly additionalProperties: false;
|
|
415
415
|
readonly properties: {
|
|
416
416
|
readonly type: {
|
|
417
|
-
readonly enum: readonly ["api", "webhook", "cron"];
|
|
417
|
+
readonly enum: readonly ["api", "webhook", "cron", "event"];
|
|
418
418
|
};
|
|
419
419
|
readonly trigger_id: {
|
|
420
420
|
readonly type: "string";
|
|
@@ -570,7 +570,7 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
|
|
|
570
570
|
readonly additionalProperties: false;
|
|
571
571
|
readonly properties: {
|
|
572
572
|
readonly type: {
|
|
573
|
-
readonly enum: readonly ["api", "webhook", "cron"];
|
|
573
|
+
readonly enum: readonly ["api", "webhook", "cron", "event"];
|
|
574
574
|
};
|
|
575
575
|
readonly trigger_id: {
|
|
576
576
|
readonly type: "string";
|
|
@@ -711,7 +711,7 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
|
|
|
711
711
|
readonly additionalProperties: false;
|
|
712
712
|
readonly properties: {
|
|
713
713
|
readonly type: {
|
|
714
|
-
readonly enum: readonly ["api", "webhook", "cron"];
|
|
714
|
+
readonly enum: readonly ["api", "webhook", "cron", "event"];
|
|
715
715
|
};
|
|
716
716
|
readonly trigger_id: {
|
|
717
717
|
readonly type: "string";
|
|
@@ -734,7 +734,7 @@ export const workflowTriggerSchema = {
|
|
|
734
734
|
type: "object",
|
|
735
735
|
additionalProperties: false,
|
|
736
736
|
properties: {
|
|
737
|
-
type: { enum: ["api", "webhook", "cron"] },
|
|
737
|
+
type: { enum: ["api", "webhook", "cron", "event"] },
|
|
738
738
|
trigger_id: { type: "string" },
|
|
739
739
|
trigger_name: { type: ["string", "null"] },
|
|
740
740
|
secret_required: { type: "boolean" },
|