@oxygen-agent/cli 1.162.10 → 1.177.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/http-client.js +2 -78
- package/dist/index.js +1312 -527
- package/dist/run-wait.d.ts +23 -0
- package/dist/run-wait.js +57 -0
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +0 -5
- package/node_modules/@oxygen/shared/dist/cell-format.d.ts +2 -14
- package/node_modules/@oxygen/shared/dist/cell-format.js +3 -10
- package/node_modules/@oxygen/shared/dist/cli-envelope.d.ts +27 -0
- package/node_modules/@oxygen/shared/dist/cli-envelope.js +102 -0
- package/node_modules/@oxygen/shared/dist/cli-result.d.ts +39 -0
- package/node_modules/@oxygen/shared/dist/cli-result.js +52 -0
- package/node_modules/@oxygen/shared/dist/credit-guidance.d.ts +0 -1
- package/node_modules/@oxygen/shared/dist/credit-guidance.js +1 -1
- package/node_modules/@oxygen/shared/dist/file-import.js +1 -1
- package/node_modules/@oxygen/shared/dist/index.d.ts +3 -39
- package/node_modules/@oxygen/shared/dist/index.js +3 -44
- package/node_modules/@oxygen/shared/dist/log.d.ts +0 -1
- package/node_modules/@oxygen/shared/dist/log.js +8 -3
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +0 -3
- package/node_modules/@oxygen/shared/dist/object-storage.js +1 -24
- package/node_modules/@oxygen/shared/dist/redaction.js +45 -4
- package/node_modules/@oxygen/shared/dist/search-vocab.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/search-vocab.js +151 -0
- package/node_modules/@oxygen/shared/dist/select-options.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/select-options.js +121 -0
- package/node_modules/@oxygen/shared/dist/sequences.js +1 -1
- package/node_modules/@oxygen/shared/dist/sql-error.d.ts +0 -6
- package/node_modules/@oxygen/shared/dist/sql-error.js +67 -58
- package/node_modules/@oxygen/shared/dist/telemetry.d.ts +0 -1
- package/node_modules/@oxygen/shared/dist/telemetry.js +23 -18
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/dist/worker-failures-queue.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/worker-failures-queue.js +56 -0
- package/node_modules/@oxygen/workflows/dist/index.d.ts +12 -11
- package/node_modules/@oxygen/workflows/dist/index.js +58 -0
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@ import { redactSqlParameters, sqlErrorTelemetryAttributes } from "./sql-error.js
|
|
|
5
5
|
import { OXYGEN_VERSION } from "./version.js";
|
|
6
6
|
const counterCache = new Map();
|
|
7
7
|
const histogramCache = new Map();
|
|
8
|
+
// skipcq: JS-0116 — `async` keeps synchronous setup throws (getTracer / attribute
|
|
9
|
+
// normalization) as rejections so the Promise<T> contract holds for all callers.
|
|
8
10
|
export async function withTelemetrySpan(tracerName, name, attributes, fn, options) {
|
|
9
11
|
const tracer = trace.getTracer(tracerName, OXYGEN_VERSION);
|
|
10
12
|
return tracer.startActiveSpan(name, { attributes: normalizeTelemetryAttributes(commonTelemetryAttributes(attributes)) }, async (span) => {
|
|
@@ -51,12 +53,6 @@ export function markActiveTelemetryError(message, attributes) {
|
|
|
51
53
|
span.setAttributes(normalizeTelemetryAttributes(attributes));
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
|
-
export function addTelemetryEvent(name, attributes) {
|
|
55
|
-
const span = trace.getActiveSpan();
|
|
56
|
-
if (!span)
|
|
57
|
-
return;
|
|
58
|
-
span.addEvent(name, normalizeTelemetryAttributes(attributes));
|
|
59
|
-
}
|
|
60
56
|
export function recordTelemetryCounter(name, value = 1, attributes) {
|
|
61
57
|
if (!Number.isFinite(value))
|
|
62
58
|
return;
|
|
@@ -71,28 +67,37 @@ export function recordTelemetryHistogram(name, value, attributes) {
|
|
|
71
67
|
}
|
|
72
68
|
export function commonTelemetryAttributes(attributes) {
|
|
73
69
|
const workerProcess = isWorkerProcess();
|
|
70
|
+
// Each attribute reads an ordered env-var fallback chain that differs by
|
|
71
|
+
// surface: the worker prefers its Fly vars first, the web its Vercel vars.
|
|
72
|
+
// pickEnvBySurface picks the surface's list, then the first defined value.
|
|
73
|
+
const pick = (workerKeys, webKeys) => pickEnvBySurface(workerProcess, workerKeys, webKeys);
|
|
74
74
|
return {
|
|
75
75
|
"oxygen.version": OXYGEN_VERSION,
|
|
76
|
-
"deployment.environment":
|
|
77
|
-
? process.env.FLY_ENVIRONMENT ?? process.env.NODE_ENV ?? null
|
|
78
|
-
: process.env.VERCEL_ENV ?? process.env.NODE_ENV ?? null,
|
|
76
|
+
"deployment.environment": pick(["FLY_ENVIRONMENT", "NODE_ENV"], ["VERCEL_ENV", "NODE_ENV"]),
|
|
79
77
|
// deployment.sha stays the deploy artifact id: the Fly registry image ref on
|
|
80
78
|
// the worker (release cross-reference), VERCEL_GIT_COMMIT_SHA on the web. The
|
|
81
79
|
// worker's image ref is NOT a git commit, so deployment.git_sha carries the
|
|
82
80
|
// resolved commit separately and is the single field that means "the commit"
|
|
83
81
|
// on both surfaces (OXY-61). On the web both happen to equal the Vercel SHA.
|
|
84
|
-
"deployment.sha":
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"deployment.git_sha": workerProcess
|
|
88
|
-
? process.env.OXYGEN_GIT_SHA ?? process.env.VERCEL_GIT_COMMIT_SHA ?? null
|
|
89
|
-
: process.env.VERCEL_GIT_COMMIT_SHA ?? process.env.OXYGEN_GIT_SHA ?? null,
|
|
90
|
-
"cloud.region": workerProcess
|
|
91
|
-
? process.env.FLY_REGION ?? process.env.VERCEL_REGION ?? null
|
|
92
|
-
: process.env.VERCEL_REGION ?? process.env.FLY_REGION ?? null,
|
|
82
|
+
"deployment.sha": pick(["FLY_IMAGE_REF", "VERCEL_GIT_COMMIT_SHA"], ["VERCEL_GIT_COMMIT_SHA", "FLY_IMAGE_REF"]),
|
|
83
|
+
"deployment.git_sha": pick(["OXYGEN_GIT_SHA", "VERCEL_GIT_COMMIT_SHA"], ["VERCEL_GIT_COMMIT_SHA", "OXYGEN_GIT_SHA"]),
|
|
84
|
+
"cloud.region": pick(["FLY_REGION", "VERCEL_REGION"], ["VERCEL_REGION", "FLY_REGION"]),
|
|
93
85
|
...attributes,
|
|
94
86
|
};
|
|
95
87
|
}
|
|
88
|
+
// Picks the env-var fallback list for the active surface (worker vs web), then
|
|
89
|
+
// returns the first env var in that list that is set, else null. Preserves the
|
|
90
|
+
// original `a ?? b ?? null` semantics exactly: only an unset (`undefined`) env
|
|
91
|
+
// var falls through to the next key — an explicit empty string is kept.
|
|
92
|
+
function pickEnvBySurface(workerProcess, workerKeys, webKeys) {
|
|
93
|
+
const keys = workerProcess ? workerKeys : webKeys;
|
|
94
|
+
for (const key of keys) {
|
|
95
|
+
const value = process.env[key];
|
|
96
|
+
if (value !== undefined)
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
96
101
|
function isWorkerProcess() {
|
|
97
102
|
if (process.env.OXYGEN_PROCESS_ROLE === "worker")
|
|
98
103
|
return true;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const OXYGEN_VERSION = "1.
|
|
1
|
+
export declare const OXYGEN_VERSION = "1.177.0";
|
|
2
2
|
export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.154.0";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.
|
|
1
|
+
export const OXYGEN_VERSION = "1.177.0";
|
|
2
2
|
// Bump this only when deployed CLI/API contracts require a newer CLI.
|
|
3
3
|
// 1.154.0: LinkedIn → Sequencer rename moved the CLI/API/MCP surface
|
|
4
4
|
// (oxygen sequences|inbox|senders, /api/cli/{sequences,inbox,senders}) and
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical queue selector for the worker-failures surface, shared by:
|
|
3
|
+
* - CLI `oxygen worker failures --queue <queue>` (free-text, passed through),
|
|
4
|
+
* - MCP `oxygen_worker_failures({ queue })` (enum-constrained), and
|
|
5
|
+
* - the `/api/cli/worker/failures` route that actually parses the value.
|
|
6
|
+
*
|
|
7
|
+
* Single-sourced so a token documented by one surface can never silently fall
|
|
8
|
+
* back to the `all` aggregate on another — the exact drift OXY-337 fixed, where
|
|
9
|
+
* the CLI documented `postgres_jobs` but the route only accepted `postgres_queue`.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Public, documented queue tokens in display order. `postgres_queue` is the
|
|
13
|
+
* canonical name for the Postgres-backed background job queue. The MCP enum is
|
|
14
|
+
* built from this list so the constraint can never drift from the parser.
|
|
15
|
+
*/
|
|
16
|
+
export declare const WORKER_FAILURES_QUEUE_VALUES: readonly ["all", "actions", "ingestions", "postgres_queue"];
|
|
17
|
+
export type WorkerFailuresQueue = (typeof WORKER_FAILURES_QUEUE_VALUES)[number];
|
|
18
|
+
/**
|
|
19
|
+
* Normalize a raw `--queue` / `queue` value to its canonical token. Empty,
|
|
20
|
+
* missing, or unrecognized values resolve to `all` (the aggregate default).
|
|
21
|
+
*/
|
|
22
|
+
export declare function normalizeWorkerFailuresQueue(value: string | null | undefined): WorkerFailuresQueue;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical queue selector for the worker-failures surface, shared by:
|
|
3
|
+
* - CLI `oxygen worker failures --queue <queue>` (free-text, passed through),
|
|
4
|
+
* - MCP `oxygen_worker_failures({ queue })` (enum-constrained), and
|
|
5
|
+
* - the `/api/cli/worker/failures` route that actually parses the value.
|
|
6
|
+
*
|
|
7
|
+
* Single-sourced so a token documented by one surface can never silently fall
|
|
8
|
+
* back to the `all` aggregate on another — the exact drift OXY-337 fixed, where
|
|
9
|
+
* the CLI documented `postgres_jobs` but the route only accepted `postgres_queue`.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Public, documented queue tokens in display order. `postgres_queue` is the
|
|
13
|
+
* canonical name for the Postgres-backed background job queue. The MCP enum is
|
|
14
|
+
* built from this list so the constraint can never drift from the parser.
|
|
15
|
+
*/
|
|
16
|
+
export const WORKER_FAILURES_QUEUE_VALUES = [
|
|
17
|
+
"all",
|
|
18
|
+
"actions",
|
|
19
|
+
"ingestions",
|
|
20
|
+
"postgres_queue",
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Every accepted token mapped to its canonical value. Covers the canonical
|
|
24
|
+
* tokens themselves plus legacy/compat aliases:
|
|
25
|
+
* - `postgres_jobs` was the CLI-documented token before this unification.
|
|
26
|
+
* - `bullmq` / `redis` / `jobs` predate the Postgres-queue migration.
|
|
27
|
+
* - `postgres` / `pg` and the singular `action` / `ingestion` are shorthands.
|
|
28
|
+
*/
|
|
29
|
+
const QUEUE_TOKEN_TO_CANONICAL = {
|
|
30
|
+
all: "all",
|
|
31
|
+
action: "actions",
|
|
32
|
+
actions: "actions",
|
|
33
|
+
table_actions: "actions",
|
|
34
|
+
ingestion: "ingestions",
|
|
35
|
+
ingestions: "ingestions",
|
|
36
|
+
table_ingestions: "ingestions",
|
|
37
|
+
postgres: "postgres_queue",
|
|
38
|
+
postgres_queue: "postgres_queue",
|
|
39
|
+
postgres_jobs: "postgres_queue",
|
|
40
|
+
pg: "postgres_queue",
|
|
41
|
+
bullmq: "postgres_queue",
|
|
42
|
+
redis: "postgres_queue",
|
|
43
|
+
jobs: "postgres_queue",
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Normalize a raw `--queue` / `queue` value to its canonical token. Empty,
|
|
47
|
+
* missing, or unrecognized values resolve to `all` (the aggregate default).
|
|
48
|
+
*/
|
|
49
|
+
export function normalizeWorkerFailuresQueue(value) {
|
|
50
|
+
if (!value)
|
|
51
|
+
return "all";
|
|
52
|
+
const normalized = value.trim().toLowerCase();
|
|
53
|
+
if (!normalized)
|
|
54
|
+
return "all";
|
|
55
|
+
return QUEUE_TOKEN_TO_CANONICAL[normalized] ?? "all";
|
|
56
|
+
}
|
|
@@ -9,7 +9,6 @@ export declare const DEFAULT_WORKFLOW_CRON_TIMEZONE = "UTC";
|
|
|
9
9
|
export type WorkflowMode = "dry_run" | "live" | "smoke_test";
|
|
10
10
|
export type WorkflowTriggerType = "api" | "webhook" | "cron" | "event";
|
|
11
11
|
export type WorkflowStatus = "active" | "disabled";
|
|
12
|
-
export type WorkflowStepKind = "transform" | "tool" | "branch";
|
|
13
12
|
export type WorkflowStepEffect = "none" | "external_read" | "external_write";
|
|
14
13
|
export type RecipeRuntime = "durable";
|
|
15
14
|
export type WorkflowEventFilterOp = "eq" | "neq" | "exists" | "not_exists";
|
|
@@ -211,16 +210,6 @@ export type Blueprint = {
|
|
|
211
210
|
source_hash: string;
|
|
212
211
|
compiler_version: typeof BLUEPRINT_COMPILER_VERSION;
|
|
213
212
|
};
|
|
214
|
-
export type WorkflowApplyInput = {
|
|
215
|
-
manifest: WorkflowManifest;
|
|
216
|
-
};
|
|
217
|
-
export type WorkflowCallInput = {
|
|
218
|
-
workflow_id?: string;
|
|
219
|
-
workflow_name?: string;
|
|
220
|
-
input?: Record<string, unknown>;
|
|
221
|
-
mode: WorkflowMode;
|
|
222
|
-
idempotency_key?: string;
|
|
223
|
-
};
|
|
224
213
|
type WorkflowFunction = (context: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
225
214
|
export type WorkflowDefinition = {
|
|
226
215
|
readonly __oxygen_workflow_definition: true;
|
|
@@ -815,4 +804,16 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
|
|
|
815
804
|
};
|
|
816
805
|
};
|
|
817
806
|
};
|
|
807
|
+
export declare const WORKFLOW_MAX_CREDITS_EXCEEDED_ERROR_CODE = "max_credits_exceeded";
|
|
808
|
+
export declare function readWorkflowRunMaxCredits(metadata: Record<string, unknown> | null | undefined): number | null;
|
|
809
|
+
export declare function readManagedToolRunCredits(output: unknown): number;
|
|
810
|
+
export type WorkflowSpendCapDecision = {
|
|
811
|
+
allowed: boolean;
|
|
812
|
+
projectedCredits: number;
|
|
813
|
+
};
|
|
814
|
+
export declare function evaluateWorkflowRunSpendCap(input: {
|
|
815
|
+
maxCredits: number;
|
|
816
|
+
creditsUsed: number;
|
|
817
|
+
estimatedCredits?: number | null;
|
|
818
|
+
}): WorkflowSpendCapDecision;
|
|
818
819
|
export {};
|
|
@@ -1393,6 +1393,64 @@ function isRecord(value) {
|
|
|
1393
1393
|
function isNonEmptyString(value) {
|
|
1394
1394
|
return typeof value === "string" && value.trim().length > 0;
|
|
1395
1395
|
}
|
|
1396
|
+
// --- Workflow run spend cap (max_credits) -------------------------------
|
|
1397
|
+
// Live `workflows call` requests carry an approved max_credits spend cap in
|
|
1398
|
+
// the run's metadata (R-E.22(c)). These pure helpers are the single source of
|
|
1399
|
+
// truth for reading that cap and deciding whether the next paid tool step may
|
|
1400
|
+
// run; the worker's step loop and the durable-recipe runtime wrap the refusal
|
|
1401
|
+
// in an OxygenError with this code so the stop is visible as run/step state.
|
|
1402
|
+
export const WORKFLOW_MAX_CREDITS_EXCEEDED_ERROR_CODE = "max_credits_exceeded";
|
|
1403
|
+
// Matches the soft-ceiling epsilon used by table action runs so float drift
|
|
1404
|
+
// never refuses a run that is exactly at its cap.
|
|
1405
|
+
const WORKFLOW_SPEND_CAP_EPSILON = 0.000001;
|
|
1406
|
+
export function readWorkflowRunMaxCredits(metadata) {
|
|
1407
|
+
if (!isRecord(metadata))
|
|
1408
|
+
return null;
|
|
1409
|
+
const raw = metadata.max_credits ?? metadata.maxCredits;
|
|
1410
|
+
const parsed = typeof raw === "number"
|
|
1411
|
+
? raw
|
|
1412
|
+
: typeof raw === "string" && raw.trim()
|
|
1413
|
+
? Number(raw)
|
|
1414
|
+
: Number.NaN;
|
|
1415
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
1416
|
+
return null;
|
|
1417
|
+
return parsed;
|
|
1418
|
+
}
|
|
1419
|
+
// Managed tool runs attach `meta.billing` with the credits the run charged
|
|
1420
|
+
// (`managed_credit_estimate` is reserved and captured in full). BYOK and
|
|
1421
|
+
// user-connection runs consume no OXYGEN credits, so they never count
|
|
1422
|
+
// against the cap.
|
|
1423
|
+
export function readManagedToolRunCredits(output) {
|
|
1424
|
+
if (!isRecord(output))
|
|
1425
|
+
return 0;
|
|
1426
|
+
const meta = output.meta;
|
|
1427
|
+
if (!isRecord(meta))
|
|
1428
|
+
return 0;
|
|
1429
|
+
const billing = meta.billing;
|
|
1430
|
+
if (!isRecord(billing))
|
|
1431
|
+
return 0;
|
|
1432
|
+
if (billing.credential_mode !== "managed")
|
|
1433
|
+
return 0;
|
|
1434
|
+
const credits = billing.managed_credit_estimate;
|
|
1435
|
+
if (typeof credits !== "number" || !Number.isFinite(credits) || credits <= 0)
|
|
1436
|
+
return 0;
|
|
1437
|
+
return credits;
|
|
1438
|
+
}
|
|
1439
|
+
// `estimatedCredits` is null when the next tool has no managed-credit price
|
|
1440
|
+
// (free, BYOK, or unknown catalog entry): such a step is only refused when
|
|
1441
|
+
// the cap is already breached, never pre-emptively.
|
|
1442
|
+
export function evaluateWorkflowRunSpendCap(input) {
|
|
1443
|
+
const estimated = typeof input.estimatedCredits === "number"
|
|
1444
|
+
&& Number.isFinite(input.estimatedCredits)
|
|
1445
|
+
&& input.estimatedCredits > 0
|
|
1446
|
+
? input.estimatedCredits
|
|
1447
|
+
: 0;
|
|
1448
|
+
const projectedCredits = input.creditsUsed + estimated;
|
|
1449
|
+
return {
|
|
1450
|
+
allowed: projectedCredits <= input.maxCredits + WORKFLOW_SPEND_CAP_EPSILON,
|
|
1451
|
+
projectedCredits,
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1396
1454
|
function escapeRegExp(value) {
|
|
1397
1455
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1398
1456
|
}
|