@oxygen-agent/cli 1.177.1 → 1.209.6

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.
Files changed (31) hide show
  1. package/README.md +1 -1
  2. package/dist/http-client.js +6 -4
  3. package/dist/index.d.ts +0 -1
  4. package/dist/index.js +1144 -24
  5. package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +41 -0
  6. package/node_modules/@oxygen/shared/dist/billing.d.ts +28 -6
  7. package/node_modules/@oxygen/shared/dist/billing.js +41 -0
  8. package/node_modules/@oxygen/shared/dist/budget-scopes.d.ts +4 -0
  9. package/node_modules/@oxygen/shared/dist/budget-scopes.js +9 -0
  10. package/node_modules/@oxygen/shared/dist/cell-format.d.ts +6 -0
  11. package/node_modules/@oxygen/shared/dist/cell-format.js +26 -0
  12. package/node_modules/@oxygen/shared/dist/index.d.ts +2 -0
  13. package/node_modules/@oxygen/shared/dist/index.js +2 -0
  14. package/node_modules/@oxygen/shared/dist/networks.d.ts +21 -0
  15. package/node_modules/@oxygen/shared/dist/networks.js +25 -0
  16. package/node_modules/@oxygen/shared/dist/search-vocab.d.ts +1 -1
  17. package/node_modules/@oxygen/shared/dist/select-options.d.ts +7 -0
  18. package/node_modules/@oxygen/shared/dist/select-options.js +9 -0
  19. package/node_modules/@oxygen/shared/dist/sequences.d.ts +91 -1
  20. package/node_modules/@oxygen/shared/dist/sequences.js +288 -15
  21. package/node_modules/@oxygen/shared/dist/signup-lead-webhook.d.ts +39 -0
  22. package/node_modules/@oxygen/shared/dist/signup-lead-webhook.js +78 -0
  23. package/node_modules/@oxygen/shared/dist/sql-error.d.ts +12 -0
  24. package/node_modules/@oxygen/shared/dist/sql-error.js +15 -0
  25. package/node_modules/@oxygen/shared/dist/version.d.ts +2 -2
  26. package/node_modules/@oxygen/shared/dist/version.js +4 -2
  27. package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.js +2 -5
  28. package/node_modules/@oxygen/workflows/dist/index.d.ts +23 -0
  29. package/node_modules/@oxygen/workflows/dist/index.js +199 -24
  30. package/oxygen.js +2 -0
  31. package/package.json +2 -2
@@ -8,6 +8,9 @@ export const BLUEPRINT_COMPILER_VERSION = "oxygen-blueprints-v1";
8
8
  export const MAX_RECIPE_BUNDLE_BYTES = 2_000_000;
9
9
  export const MAX_BLUEPRINT_BYTES = 4_000_000;
10
10
  export const DEFAULT_WORKFLOW_CRON_TIMEZONE = "UTC";
11
+ // Step ids that must not be used: they collide with JavaScript object internals
12
+ // when the runtime assembles ctx.steps, so their outputs would be silently lost.
13
+ const RESERVED_STEP_IDS = new Set(["__proto__", "constructor", "prototype"]);
11
14
  // Compatibility and determinism lint only. The Vercel sandbox process,
12
15
  // denied network policy, and runtime global guards are the security boundary.
13
16
  const UNSAFE_RECIPE_BUNDLE_PATTERNS = [
@@ -18,7 +21,11 @@ const UNSAFE_RECIPE_BUNDLE_PATTERNS = [
18
21
  { token: "XMLHttpRequest", pattern: /\bXMLHttpRequest\b/ }, // skipcq: SCT-A000
19
22
  { token: "WebSocket", pattern: /\bWebSocket\b/ },
20
23
  { token: "EventSource", pattern: /\bEventSource\b/ }, // skipcq: SCT-A000
21
- { token: "require", pattern: /\brequire\b/ },
24
+ // Block CommonJS require(...) but allow a `.require` method access (the
25
+ // ctx.approvals.require() durable-approval API). A real global require is a
26
+ // bare identifier; any X.require on a dangerous global (globalThis/process/…)
27
+ // is already caught by that global's own rule above.
28
+ { token: "require", pattern: /(?<!\.)\brequire\b/ },
22
29
  { token: "dynamic import", pattern: /\bimport(?:\s|\/\*[\s\S]*?\*\/)*\(/ }, // skipcq: SCT-A000
23
30
  { token: "node module import", pattern: /\b(?:node:)?(?:fs|child_process|net|tls|http|https|dns|dgram|worker_threads)\b/ }, // skipcq: SCT-A000
24
31
  { token: "eval", pattern: /\beval\b/ },
@@ -44,6 +51,7 @@ export function defineWorkflow(input) {
44
51
  ...(input.specification ? { specification: input.specification } : {}),
45
52
  ...(input.trigger ? { trigger: input.trigger } : {}),
46
53
  ...(input.inputSchema ? { inputSchema: input.inputSchema } : {}),
54
+ ...(input.maxCredits != null ? { maxCredits: input.maxCredits } : {}),
47
55
  steps: input.steps,
48
56
  };
49
57
  }
@@ -85,18 +93,49 @@ export function eventTrigger(input) {
85
93
  ...(input.status ? { status: input.status } : {}),
86
94
  };
87
95
  }
96
+ // Lookahead window for the next cron run. Must exceed the largest gap a *valid*
97
+ // schedule can have, or legitimate sparse crons get falsely rejected: the worst
98
+ // case is "Feb 29" across a century boundary (e.g. 2096 → 2104, an ~8-year gap,
99
+ // since 2100 is not a leap year). The day-skipping scan below keeps even this
100
+ // wide window cheap (~one zonedDateParts call per non-matching day).
101
+ const MAX_CRON_LOOKAHEAD_DAYS = 366 * 8 + 2;
88
102
  export function nextCronRunAfter(input) {
89
103
  const schedule = parseCronSchedule(input.cron);
90
104
  const timezone = input.timezone?.trim() || DEFAULT_WORKFLOW_CRON_TIMEZONE;
91
105
  let candidate = roundUpToNextMinute(input.after ?? new Date());
92
- const deadline = candidate.getTime() + 370 * 24 * 60 * 60 * 1000;
106
+ const deadline = candidate.getTime() + MAX_CRON_LOOKAHEAD_DAYS * 24 * 60 * 60 * 1000;
93
107
  while (candidate.getTime() <= deadline) {
94
- if (matchesCronSchedule(schedule, zonedDateParts(candidate, timezone))) {
108
+ const parts = zonedDateParts(candidate, timezone);
109
+ if (matchesCronSchedule(schedule, parts)) {
95
110
  return candidate;
96
111
  }
97
- candidate = new Date(candidate.getTime() + 60_000);
112
+ // If the calendar date itself can't match, no minute that day matches —
113
+ // jump to the next day instead of scanning all 1440 minutes. This keeps
114
+ // sparse schedules (e.g. "0 9 29 2 *") and unsatisfiable ones fast over the
115
+ // multi-year window. The jump targets ~00:00 of the next day, so a
116
+ // subsequently matching date is scanned from its start (no missed minutes).
117
+ if (!matchesCronDate(schedule, parts)) {
118
+ const minutesToNextDay = Math.max(1, 24 * 60 - (parts.hour * 60 + parts.minute));
119
+ let next = new Date(candidate.getTime() + minutesToNextDay * 60_000);
120
+ // minutesToNextDay is a LOCAL-clock count added to a UTC instant. On a
121
+ // spring-forward day the local day is < 24h, so the jump overshoots the
122
+ // next local midnight and lands at ~01:00 — skipping an early-of-day match
123
+ // (e.g. "0 0 …"), which can push the next run a full year out. If the jump
124
+ // crossed into the next local day but past midnight, pull it back to that
125
+ // midnight. (A fall-back day is > 24h: the jump undershoots and stays on
126
+ // the same date, which the next iteration advances — left as-is.)
127
+ const landed = zonedDateParts(next, timezone);
128
+ const intoNextLocalDay = landed.hour * 60 + landed.minute;
129
+ if (landed.dayOfMonth !== parts.dayOfMonth && intoNextLocalDay > 0) {
130
+ next = new Date(next.getTime() - intoNextLocalDay * 60_000);
131
+ }
132
+ candidate = next;
133
+ }
134
+ else {
135
+ candidate = new Date(candidate.getTime() + 60_000);
136
+ }
98
137
  }
99
- throw new Error("Cron expression has no matching run time in the next 370 days.");
138
+ throw new Error(`Cron expression has no matching run time in the next ${MAX_CRON_LOOKAHEAD_DAYS} days.`);
100
139
  }
101
140
  export function transformStep(input) {
102
141
  return {
@@ -116,9 +155,23 @@ export function toolStep(input) {
116
155
  tool: input.tool,
117
156
  ...(input.effect ? { effect: input.effect } : {}),
118
157
  ...(input.mode ? { mode: input.mode } : {}),
158
+ ...(input.maxCredits != null ? { maxCredits: input.maxCredits } : {}),
119
159
  payload: input.payload,
120
160
  };
121
161
  }
162
+ /**
163
+ * Conditional step. `condition` is evaluated; control then flows by mode:
164
+ *
165
+ * - then only (no `else`, no `join`) — GUARD: truthy continues to `then` (steps
166
+ * between the branch and `then` are skipped); falsy skips straight to the end
167
+ * of the workflow (no step after the branch runs) and the run completes.
168
+ * - then + else (no `join`) — skip-until-target jump to `then`/`else`; from the
169
+ * chosen target, the remaining steps run sequentially through the end.
170
+ * - then + else + join — real if/else: the non-chosen arm is skipped and `join`
171
+ * is the convergence point where shared downstream work resumes.
172
+ *
173
+ * All targets must be ids of later steps (branches are forward-only).
174
+ */
122
175
  export function branchStep(input) {
123
176
  return {
124
177
  __oxygen_workflow_step: true,
@@ -161,6 +214,7 @@ definition, options = {}) {
161
214
  ...(definition.specification ? { specification: definition.specification } : {}),
162
215
  ...(definition.trigger ? { trigger: definition.trigger } : {}),
163
216
  ...(definition.inputSchema ? { input_schema: definition.inputSchema } : {}),
217
+ ...(definition.maxCredits != null ? { max_credits: definition.maxCredits } : {}),
164
218
  steps: definition.steps.map((step) => {
165
219
  if (step.kind === "transform") {
166
220
  return {
@@ -188,6 +242,7 @@ definition, options = {}) {
188
242
  tool: step.tool,
189
243
  effect: step.effect ?? "external_read",
190
244
  ...(step.mode ? { mode: step.mode } : {}),
245
+ ...(step.maxCredits != null ? { max_credits: step.maxCredits } : {}),
191
246
  payload_source: serializeWorkflowFunction(step.payload, `steps.${step.id}.payload`),
192
247
  };
193
248
  }),
@@ -218,12 +273,23 @@ export function buildRecipeManifest(input) {
218
273
  bundle_format: "esm",
219
274
  tools_used: Array.from(new Set((input.toolsUsed ?? []).filter(Boolean))).sort(),
220
275
  ...(input.visualPlan ? { visual_plan: input.visualPlan } : {}),
276
+ ...(input.maxCredits != null ? { max_credits: input.maxCredits } : {}),
221
277
  source_hash: sourceHash,
222
278
  compiler_version: DURABLE_RECIPE_COMPILER_VERSION,
223
279
  created_at: (input.createdAt ?? new Date()).toISOString(),
224
280
  };
225
281
  return manifest;
226
282
  }
283
+ // Shared positive-finite validator for the optional per-run / per-step
284
+ // max_credits caps (Gap 2). `undefined` is valid (no cap); anything else must be
285
+ // a positive finite number, or lint rejects the manifest.
286
+ function validateOptionalMaxCredits(value, path, add) {
287
+ if (value === undefined)
288
+ return;
289
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
290
+ add(path, "invalid_max_credits", "max_credits must be a positive number.");
291
+ }
292
+ }
227
293
  export function lintWorkflowManifest(// skipcq: JS-R1005
228
294
  value, options = {}) {
229
295
  if (isRecord(value)
@@ -255,6 +321,7 @@ value, options = {}) {
255
321
  }
256
322
  if (value.trigger !== undefined)
257
323
  validateTrigger(value.trigger, "$.trigger", add);
324
+ validateOptionalMaxCredits(value.max_credits, "$.max_credits", add);
258
325
  if (!Array.isArray(value.steps) || value.steps.length === 0) {
259
326
  add("$.steps", "missing_steps", "At least one workflow step is required.");
260
327
  }
@@ -278,6 +345,15 @@ value, options = {}) {
278
345
  if (!isNonEmptyString(step.id)) {
279
346
  add(`${path}.id`, "invalid_step_id", "Step id is required.");
280
347
  }
348
+ else if (RESERVED_STEP_IDS.has(step.id)) {
349
+ // Step ids become keys on the ctx.steps object the runtime assembles.
350
+ // "__proto__" / "constructor" / "prototype" collide with JavaScript
351
+ // object internals: assigning ctx.steps["__proto__"] = output hits the
352
+ // prototype setter, so the output is silently dropped from step outputs
353
+ // and unreadable downstream while the run still reports success. Reject
354
+ // at authoring time instead of letting that data loss happen at runtime.
355
+ add(`${path}.id`, "reserved_step_id", `Step id '${step.id}' is reserved (it collides with a JavaScript object key and would be dropped from step outputs). Choose another id.`);
356
+ }
281
357
  else if (ids.has(step.id)) {
282
358
  add(`${path}.id`, "duplicate_step_id", `Step id '${step.id}' is duplicated.`);
283
359
  }
@@ -320,6 +396,7 @@ value, options = {}) {
320
396
  && step.mode !== "smoke_test") {
321
397
  add(`${path}.mode`, "invalid_step_mode", "Step mode is invalid.");
322
398
  }
399
+ validateOptionalMaxCredits(step.max_credits, `${path}.max_credits`, add);
323
400
  return;
324
401
  }
325
402
  if (step.kind === "branch") {
@@ -390,6 +467,7 @@ value, options = {}) {
390
467
  if (value.bundle_format !== "esm") {
391
468
  add("$.bundle_format", "invalid_bundle_format", "Recipe bundle_format must be 'esm'.");
392
469
  }
470
+ validateOptionalMaxCredits(value.max_credits, "$.max_credits", add);
393
471
  if (value.runtime !== "durable") {
394
472
  add("$.runtime", "invalid_recipe_runtime", "Durable recipe manifests must set runtime to durable.");
395
473
  }
@@ -802,25 +880,52 @@ export async function runPureWorkflowFunction(input) {
802
880
  sandbox.setImmediate = undefined;
803
881
  sandbox.queueMicrotask = undefined;
804
882
  sandbox.WebAssembly = undefined;
805
- const script = new vm.Script(`"use strict";\n`
806
- + `const __oxygen_context = JSON.parse(__oxygen_context_json);\n`
883
+ const script = new vm.Script('"use strict";\n'
884
+ + 'const __oxygen_context = JSON.parse(__oxygen_context_json);\n'
807
885
  + `const __oxygen_fn = (${input.source});\n`
808
- + `__oxygen_fn(__oxygen_context);`);
886
+ + '__oxygen_fn(__oxygen_context);');
809
887
  // Disable dynamic code generation in the sandboxed context. Combined with
810
888
  // the JSON re-parse above, this means every prototype-walk path to
811
889
  // Function — direct, via context.constructor.constructor, or any other
812
890
  // reachable Function instance — throws EvalError when called with source.
813
- const result = script.runInNewContext(sandbox, {
814
- timeout: timeoutMs,
815
- contextCodeGeneration: {
816
- strings: false,
817
- wasm: false,
818
- },
819
- });
820
- const resolved = isPromiseLike(result)
821
- ? await withTimeout(result, timeoutMs)
822
- : result;
823
- return enforceJsonOutput(resolved, maxOutputBytes);
891
+ try {
892
+ const result = script.runInNewContext(sandbox, {
893
+ timeout: timeoutMs,
894
+ contextCodeGeneration: {
895
+ strings: false,
896
+ wasm: false,
897
+ },
898
+ });
899
+ const resolved = isPromiseLike(result)
900
+ ? await withTimeout(result, timeoutMs)
901
+ : result;
902
+ return enforceJsonOutput(resolved, maxOutputBytes);
903
+ }
904
+ catch (error) {
905
+ throw normalizeSandboxThrow(error);
906
+ }
907
+ }
908
+ // A throw from inside the vm sandbox is constructed with the sandbox realm's
909
+ // Error, so `error instanceof Error` is false in the host worker — and
910
+ // serializeOxygenError then drops the message and reports a generic
911
+ // "Workflow run failed.", leaving a failing transform/branch undebuggable.
912
+ // Reconstruct a host-realm Error preserving the user's message/name. Host-realm
913
+ // throws (vm timeout, enforceJsonOutput's workflow_output_too_large) already
914
+ // pass instanceof and are returned unchanged.
915
+ function normalizeSandboxThrow(error) {
916
+ if (error instanceof Error)
917
+ return error;
918
+ if (error && typeof error === "object") {
919
+ const record = error;
920
+ const message = typeof record.message === "string" && record.message
921
+ ? record.message
922
+ : "Workflow function threw.";
923
+ const normalized = new Error(message);
924
+ if (typeof record.name === "string" && record.name)
925
+ normalized.name = record.name;
926
+ return normalized;
927
+ }
928
+ return new Error(typeof error === "string" && error ? error : "Workflow function threw a non-error value.");
824
929
  }
825
930
  export const workflowApplySchema = {
826
931
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -1077,6 +1182,19 @@ value, path, add) {
1077
1182
  add(`${path}.cron`, "invalid_cron", error instanceof Error ? error.message : "Cron expression is invalid.");
1078
1183
  }
1079
1184
  }
1185
+ // Validate the timezone here so an unknown zone fails lint with a typed
1186
+ // issue, rather than passing lint and blowing up at apply time (the
1187
+ // scheduler's Intl.DateTimeFormat throws on an unknown zone). null/empty
1188
+ // are allowed — the scheduler falls back to UTC.
1189
+ const { timezone } = value;
1190
+ if (timezone !== undefined && timezone !== null) {
1191
+ if (typeof timezone !== "string") {
1192
+ add(`${path}.timezone`, "invalid_timezone", "Cron trigger timezone must be a string IANA timezone.");
1193
+ }
1194
+ else if (timezone.trim() && !isValidTimeZone(timezone.trim())) {
1195
+ add(`${path}.timezone`, "invalid_timezone", `Unknown timezone '${timezone}'. Use an IANA timezone such as 'America/New_York' or 'UTC'.`);
1196
+ }
1197
+ }
1080
1198
  return;
1081
1199
  }
1082
1200
  if (value.type === "event") {
@@ -1272,11 +1390,9 @@ function normalizeDaysOfWeek(values) {
1272
1390
  }
1273
1391
  return normalized;
1274
1392
  }
1275
- function matchesCronSchedule(schedule, parts) {
1276
- if (!schedule.minutes.has(parts.minute))
1277
- return false;
1278
- if (!schedule.hours.has(parts.hour))
1279
- return false;
1393
+ // Date-only match (month + day-of-month/day-of-week), excluding hour/minute.
1394
+ // Lets nextCronRunAfter skip whole non-matching days in one jump.
1395
+ function matchesCronDate(schedule, parts) {
1280
1396
  if (!schedule.months.has(parts.month))
1281
1397
  return false;
1282
1398
  const dayOfMonthMatches = schedule.daysOfMonth.has(parts.dayOfMonth);
@@ -1286,6 +1402,13 @@ function matchesCronSchedule(schedule, parts) {
1286
1402
  }
1287
1403
  return dayOfMonthMatches && dayOfWeekMatches;
1288
1404
  }
1405
+ function matchesCronSchedule(schedule, parts) {
1406
+ if (!schedule.minutes.has(parts.minute))
1407
+ return false;
1408
+ if (!schedule.hours.has(parts.hour))
1409
+ return false;
1410
+ return matchesCronDate(schedule, parts);
1411
+ }
1289
1412
  function roundUpToNextMinute(value) {
1290
1413
  const result = new Date(value.getTime());
1291
1414
  result.setUTCSeconds(0, 0);
@@ -1309,6 +1432,17 @@ function zonedDateParts(date, timezone) {
1309
1432
  dayOfWeek: weekdayToNumber(parts.get("weekday") ?? ""),
1310
1433
  };
1311
1434
  }
1435
+ function isValidTimeZone(timezone) {
1436
+ try {
1437
+ // Throws RangeError for an unknown IANA zone; same mechanism the scheduler
1438
+ // uses, so lint accepts exactly the zones the runtime can resolve.
1439
+ new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format(0);
1440
+ return true;
1441
+ }
1442
+ catch {
1443
+ return false;
1444
+ }
1445
+ }
1312
1446
  function getZonedFormatter(timezone) {
1313
1447
  const cached = zonedFormatters.get(timezone);
1314
1448
  if (cached)
@@ -1349,6 +1483,25 @@ function weekdayToNumber(value) {
1349
1483
  function hashWorkflowSource(source) {
1350
1484
  return createHash("sha256").update(source).digest("hex");
1351
1485
  }
1486
+ // Deterministic plan hash (Gap 4): binds an approval decision to the exact
1487
+ // reviewed plan + cost so it cannot be replayed against a changed plan. The
1488
+ // durable mid-run approval gate (recipe-context) hashes its relevant fields
1489
+ // through this; binding the imperative `workflows call` admission path the same
1490
+ // way is a flagged follow-up. Canonical (sorted-key) JSON keeps the hash stable
1491
+ // regardless of key order or undefined fields.
1492
+ export function computeWorkflowPlanHash(input) {
1493
+ return hashWorkflowSource(canonicalJsonStringify(input));
1494
+ }
1495
+ function canonicalJsonStringify(value) {
1496
+ if (value === null || typeof value !== "object")
1497
+ return JSON.stringify(value) ?? "null";
1498
+ if (Array.isArray(value))
1499
+ return `[${value.map(canonicalJsonStringify).join(",")}]`;
1500
+ const entries = Object.entries(value)
1501
+ .filter(([, entryValue]) => entryValue !== undefined)
1502
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
1503
+ return `{${entries.map(([key, entryValue]) => `${JSON.stringify(key)}:${canonicalJsonStringify(entryValue)}`).join(",")}}`;
1504
+ }
1352
1505
  function workflowJsonReplacer(_key, value) {
1353
1506
  return typeof value === "function" ? Function.prototype.toString.call(value) : value;
1354
1507
  }
@@ -1416,6 +1569,28 @@ export function readWorkflowRunMaxCredits(metadata) {
1416
1569
  return null;
1417
1570
  return parsed;
1418
1571
  }
1572
+ // Approval labels the run's enqueue route already collected SYNCHRONOUS human
1573
+ // approval for (a CLI/MCP `approved=true` gate before the run was created), and
1574
+ // recorded in run metadata as `preapproved_labels`. A live ctx.approvals.require()
1575
+ // call whose `label` is in this set is honored immediately by both recipe
1576
+ // executors instead of parking on a durable decision row (local) or failing
1577
+ // closed with approval_requires_local_executor (sandbox). Every other label
1578
+ // still goes through the full durable gate, and the max_credits spend cap is
1579
+ // enforced separately per paid tool step, so a pre-approved label never widens
1580
+ // spend. Only trusted server code (the enqueue route) sets this — it is read
1581
+ // from run metadata, never from user-suppliable workflow input.
1582
+ export function readWorkflowRunPreApprovedLabels(metadata) {
1583
+ if (!isRecord(metadata))
1584
+ return [];
1585
+ const raw = metadata.preapproved_labels ?? metadata.preApprovedLabels;
1586
+ if (!Array.isArray(raw))
1587
+ return [];
1588
+ const labels = raw
1589
+ .filter((entry) => typeof entry === "string")
1590
+ .map((entry) => entry.trim())
1591
+ .filter((entry) => entry.length > 0);
1592
+ return Array.from(new Set(labels));
1593
+ }
1419
1594
  // Managed tool runs attach `meta.billing` with the credits the run charged
1420
1595
  // (`managed_credit_estimate` is reserved and captured in full). BYOK and
1421
1596
  // user-connection runs consume no OXYGEN credits, so they never count
package/oxygen.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "./dist/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxygen-agent/cli",
3
- "version": "1.177.1",
3
+ "version": "1.209.6",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -12,7 +12,7 @@
12
12
  "directory": "packages/cli"
13
13
  },
14
14
  "bin": {
15
- "oxygen": "./dist/index.js"
15
+ "oxygen": "./oxygen.js"
16
16
  },
17
17
  "exports": {
18
18
  ".": {