@overaihq/cli 0.14.0 → 0.14.2

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 CHANGED
@@ -85,6 +85,26 @@ at once without changing anything shared. Inside a clone, commands use its
85
85
  organization automatically. Precedence is `--org`, then `OVERAI_ORG_ID`, then the
86
86
  clone, then the saved default.
87
87
 
88
+ If you belong to many organizations, name one every time. The saved default is
89
+ machine-global, so it is whichever organization some other shell, editor, or
90
+ agent last switched to — and a command that falls back to it reads the wrong
91
+ tenant rather than failing. Give the orgs you work in aliases and pass `--org`:
92
+
93
+ ```sh
94
+ overai org alias acme fcps:org_abc # once
95
+ overai org alias # what you have named
96
+ overai --org acme push # every time
97
+ export OVERAI_REQUIRE_EXPLICIT_ORG=1 # make forgetting an error
98
+ ```
99
+
100
+ `OVERAI_REQUIRE_EXPLICIT_ORG=1` refuses to run any command that acts on an
101
+ organization until one is named, whether by `--org`, `OVERAI_ORG_ID`, or the
102
+ clone you are standing in. That includes `clone`, which reaches into an org, and
103
+ bare `signout`, which revokes one — `signout <ref>` and `signout --all` are
104
+ explicit already. `signin`, `whoami`, `org`, `remote`, `best-practices`, `help`,
105
+ `version`, and `update` stay available: the first four are how you find the org
106
+ to name, and the rest touch none.
107
+
88
108
  Missing org sessions are established from an eligible saved CLI login. SSO requires
89
109
  browser authorization, and the prompt names the exact command to run. Ordinary
90
110
  commands never launch a browser. `whoami` shows the remote, org, identity, session
package/dist/index.js CHANGED
@@ -26665,6 +26665,7 @@ var leadingFlag = (name2) => {
26665
26665
  return void 0;
26666
26666
  };
26667
26667
  var LEADING_FLAGS = ["--org"];
26668
+ var requireExplicitOrg = process.env.OVERAI_REQUIRE_EXPLICIT_ORG === "1";
26668
26669
  var orgRefOverride = leadingFlag("--org") ?? (process.env.OVERAI_ORG_ID != null && process.env.OVERAI_ORG_ID !== "" ? process.env.OVERAI_ORG_ID : void 0);
26669
26670
  var orgRefSource = leadingFlag("--org") != null ? "--org" : orgRefOverride != null ? "OVERAI_ORG_ID" : void 0;
26670
26671
  var appBaseUrl = (backend) => {
@@ -26918,11 +26919,12 @@ var scopedOrgId = () => {
26918
26919
  return null;
26919
26920
  }
26920
26921
  };
26922
+ var orgScopeSource = () => orgOverride()?.source ?? (orgScopeOverride != null ? ".overai/manifest.json" : "saved default");
26921
26923
  var announceOrgScope = () => {
26922
26924
  const org = scopedOrgId();
26923
26925
  if (org == null) return;
26924
26926
  process.stderr.write(
26925
- `Using org ${org} on ${remoteName(apiBaseUrl())} (${orgOverride()?.source ?? (orgScopeOverride != null ? ".overai/manifest.json" : "saved default")}).
26927
+ `Using org ${org} on ${remoteName(apiBaseUrl())} (${orgScopeSource()}).
26926
26928
  `
26927
26929
  );
26928
26930
  };
@@ -27131,7 +27133,7 @@ var v2Error = (label, res, rawBody) => {
27131
27133
  const claimed = (typeof field === "string" ? field : rawBody).trim().slice(0, 500);
27132
27134
  const reason = claimed.length === 0 || claimed === "Error" || claimed === "null" || claimed === "undefined" || claimed.toLowerCase() === STATUS_TEXT[res.status]?.toLowerCase() ? null : claimed;
27133
27135
  const orgHint = res.status === 404 || res.status === 403 ? `
27134
- If it lives in another org, prefix the command with OVERAI_ORG_ID=<orgId> (\`overai org list\` shows them).` : "";
27136
+ If it lives in another org, name that org: overai --org <ref> ... (\`overai org list\` shows them, \`overai org alias\` your shorthands).` : "";
27135
27137
  if (reason != null) return new Error(`${reason}${orgHint}`);
27136
27138
  const path = (() => {
27137
27139
  try {
@@ -27571,6 +27573,10 @@ var signOut = async (all = false, target) => {
27571
27573
  setOrgScope(manifest.organizationId);
27572
27574
  }
27573
27575
  }
27576
+ if (requireExplicitOrg && !all && orgScopeSource() === "saved default")
27577
+ throw new Error(
27578
+ "OVERAI_REQUIRE_EXPLICIT_ORG is set and nothing named an org to sign out of. Name one: overai signout <ref>, or revoke every saved session with: overai signout --all"
27579
+ );
27574
27580
  const baseUrl = apiBaseUrl();
27575
27581
  const orgId = scopedOrgId();
27576
27582
  await withStoreLock(async (store) => {
@@ -27808,7 +27814,7 @@ var runAutomation = async (args) => {
27808
27814
  // src/workspace/validateElements.ts
27809
27815
  var findings = (parsed) => parsed.success ? [] : parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
27810
27816
  var validateAutomationElements = async (elements) => {
27811
- const { automationNodesSchema } = await import("./types-3BS55ING.js");
27817
+ const { automationNodesSchema } = await import("./types-7JF7GZMJ.js");
27812
27818
  return findings(automationNodesSchema.safeParse(elements));
27813
27819
  };
27814
27820
  var validateWebPageConfig = async (config) => {
@@ -35348,6 +35354,7 @@ var generateRandomVariableName = customAlphabet(
35348
35354
 
35349
35355
  // ../common/src/ids.ts
35350
35356
  var ID_PREFIXES = {
35357
+ action: { prefix: "act", body: 20 },
35351
35358
  action_runner: { prefix: "ar", body: 10 },
35352
35359
  action_runner_email_alert: { prefix: "area", body: 10 },
35353
35360
  action_runner_email_subscription: { prefix: "ares", body: 10 },
@@ -36589,7 +36596,11 @@ Organization context: Commands in a clone select its org automatically. Outside
36589
36596
  put --org <ref> before the command (overai --org acme sql ...), or set OVERAI_ORG_ID for a
36590
36597
  whole shell. A ref is an org id, a local alias, or <remote>:<orgId>, and a named org
36591
36598
  selects its own deployment \u2014 never switch the machine-global default to reach another org,
36592
- because every other session on the machine shares it. Each remote/org has an independent
36599
+ because every other session on the machine shares it. Prefer naming the org on every
36600
+ command rather than relying on the saved default: the default is whatever another session
36601
+ last switched to, so falling back to it reads the wrong tenant instead of failing.
36602
+ OVERAI_REQUIRE_EXPLICIT_ORG=1 turns that fallback into an error for every command
36603
+ that acts on an org. Each remote/org has an independent
36593
36604
  saved session. Use whoami to inspect the effective scope. If browser authorization is
36594
36605
  required, ask the user to run the suggested signin command. Never change org to work
36595
36606
  around a clone-sync mismatch; use the correct workspace folder.
@@ -37343,6 +37354,102 @@ var runSql = async (args) => {
37343
37354
  }
37344
37355
  };
37345
37356
 
37357
+ // src/commands/storage.ts
37358
+ import { openAsBlob, writeFileSync as writeFileSync6 } from "fs";
37359
+ var USAGE3 = "Usage: overai storage upload <path> [--no-random-prefix] | storage download <key> [path|-] | storage exists <key> | storage head <key> [--lines N] | storage presign <key> [--expires-in <seconds>]";
37360
+ var runStorage = async (args) => {
37361
+ const [operation, first2, second] = args;
37362
+ if (operation == null) throw new Error(USAGE3);
37363
+ const { organizationId } = await getAccessToken();
37364
+ const segment = (key) => encodeURIComponent(key);
37365
+ if (operation === "upload") {
37366
+ if (first2 == null || first2 === "-") {
37367
+ throw new Error("Upload requires a file path (stdin uploads need a filename to build the key).");
37368
+ }
37369
+ const randomPrefix = !args.includes("--no-random-prefix");
37370
+ const query = randomPrefix ? "" : "?disableRandomPrefix=true";
37371
+ const response = await v2Fetch(
37372
+ `/v2/storage/${organizationId}/stream${query}`,
37373
+ {
37374
+ method: "POST",
37375
+ headers: {
37376
+ "Content-Type": "application/octet-stream",
37377
+ "x-file-name": first2.split("/").pop() ?? first2
37378
+ },
37379
+ body: await openAsBlob(first2)
37380
+ }
37381
+ );
37382
+ if (!response.ok) {
37383
+ throw v2Error("storage upload", response, await response.text());
37384
+ }
37385
+ const { path } = await response.json();
37386
+ const key = path.startsWith(`${organizationId}/`) ? path.slice(organizationId.length + 1) : path;
37387
+ const format2 = resolveFormat(flagValue(args, "-o", "--output"));
37388
+ if (format2 === "json") printResult({ key, path }, format2);
37389
+ else process.stdout.write(`${key}
37390
+ `);
37391
+ return;
37392
+ }
37393
+ if (first2 == null) throw new Error(USAGE3);
37394
+ if (operation === "download") {
37395
+ const response = await v2Fetch(
37396
+ `/v2/storage/${organizationId}/${segment(first2)}`,
37397
+ { redirect: "follow" }
37398
+ );
37399
+ if (!response.ok) {
37400
+ throw v2Error("storage download", response, await response.text());
37401
+ }
37402
+ const bytes = Buffer.from(await response.arrayBuffer());
37403
+ if (second != null && second !== "-") writeFileSync6(second, bytes);
37404
+ else process.stdout.write(bytes);
37405
+ return;
37406
+ }
37407
+ if (operation === "exists") {
37408
+ const response = await v2Fetch(
37409
+ `/v2/storage/${organizationId}/${segment(first2)}/exists`
37410
+ );
37411
+ if (response.status === 404) {
37412
+ if (resolveFormat(flagValue(args, "-o", "--output")) === "json") {
37413
+ printResult({ exists: false }, "json");
37414
+ }
37415
+ process.exitCode = 1;
37416
+ return;
37417
+ }
37418
+ if (!response.ok) {
37419
+ throw v2Error("storage exists", response, await response.text());
37420
+ }
37421
+ printResult(
37422
+ { exists: true, ...await response.json() },
37423
+ resolveFormat(flagValue(args, "-o", "--output"))
37424
+ );
37425
+ return;
37426
+ }
37427
+ if (operation === "head") {
37428
+ const lines = flagValue(args, "--lines") ?? "50";
37429
+ const response = await v2Fetch(
37430
+ `/v2/storage/${organizationId}/${segment(first2)}/head?numLines=${encodeURIComponent(lines)}`
37431
+ );
37432
+ if (!response.ok) {
37433
+ throw v2Error("storage head", response, await response.text());
37434
+ }
37435
+ process.stdout.write(await response.text());
37436
+ return;
37437
+ }
37438
+ if (operation === "presign") {
37439
+ const expiresIn2 = flagValue(args, "--expires-in") ?? String(60 * 60 * 24 * 7);
37440
+ const response = await v2Fetch(
37441
+ `/v2/storage/${organizationId}/${segment(first2)}/presign?expiresIn=${encodeURIComponent(expiresIn2)}`
37442
+ );
37443
+ if (!response.ok) {
37444
+ throw v2Error("storage presign", response, await response.text());
37445
+ }
37446
+ process.stdout.write(`${(await response.text()).trim()}
37447
+ `);
37448
+ return;
37449
+ }
37450
+ throw new Error(USAGE3);
37451
+ };
37452
+
37346
37453
  // src/commands/workspace.ts
37347
37454
  var workspaceIdFrom = (args) => args.find(
37348
37455
  (arg, index) => !arg.startsWith("-") && args[index - 1] !== "-o" && args[index - 1] !== "--output"
@@ -37426,7 +37533,7 @@ var listWorkspaceUsers = async (args) => {
37426
37533
  resolveFormat(flagValue(args, "-o", "--output"))
37427
37534
  );
37428
37535
  };
37429
- var USAGE3 = `Usage:
37536
+ var USAGE4 = `Usage:
37430
37537
  overai workspace list [--archived] [--limit <n>]
37431
37538
  overai workspace show [workspaceId]
37432
37539
  overai workspace users [workspaceId]
@@ -37444,7 +37551,7 @@ var runWorkspace = async (args) => {
37444
37551
  await listWorkspaceUsers(args.slice(1));
37445
37552
  return;
37446
37553
  }
37447
- process.stderr.write(USAGE3);
37554
+ process.stderr.write(USAGE4);
37448
37555
  process.exitCode = 1;
37449
37556
  };
37450
37557
 
@@ -37606,6 +37713,9 @@ Other resources:
37606
37713
  overai data-stream query <id> --start <date> --end <date> [--query <filter>]
37607
37714
  overai data-file upload <id> <path> [-m <message>]
37608
37715
  overai data-file download <id> [path|-] | data-file head <id>
37716
+ overai storage upload <path> [--no-random-prefix]
37717
+ overai storage download <key> [path|-] | storage exists <key>
37718
+ overai storage head <key> [--lines N] | storage presign <key> [--expires-in <seconds>]
37609
37719
  overai knowledge-base upload <id> <file...>
37610
37720
  overai knowledge-base search <id> <query> [--limit N]
37611
37721
  overai integration configure <id> --config <json|@file|@->
@@ -37644,6 +37754,9 @@ Output: table on a TTY, JSON when piped; -o json|table|csv to force.
37644
37754
  Aliases: login = signin, logout = signout, ws = workspace.
37645
37755
  Env: OVERAI_API_URL, OVERAI_APP_URL override the target deployment.
37646
37756
  OVERAI_ORG_ID overrides the organization for this shell.
37757
+ OVERAI_REQUIRE_EXPLICIT_ORG=1 refuses to fall back to the saved default
37758
+ org, so every command that acts on one must name it with --org or stand in
37759
+ a clone.
37647
37760
  Global flags (before the command): --org <ref>
37648
37761
  `;
37649
37762
  process.stdout.on("error", (error) => {
@@ -37668,6 +37781,24 @@ var ORG_AGNOSTIC_COMMANDS = /* @__PURE__ */ new Set([
37668
37781
  "--help",
37669
37782
  "-h"
37670
37783
  ]);
37784
+ var ORG_OPTIONAL_COMMANDS = /* @__PURE__ */ new Set([
37785
+ "signin",
37786
+ "login",
37787
+ "signout",
37788
+ "logout",
37789
+ "whoami",
37790
+ "org",
37791
+ "remote",
37792
+ "best-practices",
37793
+ "update",
37794
+ "upgrade",
37795
+ "version",
37796
+ "--version",
37797
+ "-v",
37798
+ "help",
37799
+ "--help",
37800
+ "-h"
37801
+ ]);
37671
37802
  var SKIP_UPDATE_CHECK = /* @__PURE__ */ new Set([
37672
37803
  void 0,
37673
37804
  "update",
@@ -37696,10 +37827,13 @@ var run = async () => {
37696
37827
  throw new Error(
37697
37828
  `${misplaced} goes before the command: overai ${misplaced} <ref> ${command2 ?? "<command>"} ...`
37698
37829
  );
37699
- if (command2 != null && !ORG_AGNOSTIC_COMMANDS.has(command2)) {
37700
- pinOrgFromClone();
37701
- announceOrgScope();
37702
- }
37830
+ const orgFromFolder = command2 != null && !ORG_AGNOSTIC_COMMANDS.has(command2);
37831
+ if (orgFromFolder) pinOrgFromClone();
37832
+ if (requireExplicitOrg && command2 != null && !ORG_OPTIONAL_COMMANDS.has(command2) && orgScopeSource() === "saved default")
37833
+ throw new Error(
37834
+ `OVERAI_REQUIRE_EXPLICIT_ORG is set and nothing named an org for "${command2}". Put --org <ref> before the command, or run it inside a clone. See your shorthands with: overai org alias`
37835
+ );
37836
+ if (orgFromFolder) announceOrgScope();
37703
37837
  if (rest.includes("--recursive")) {
37704
37838
  if (command2 !== "status" && command2 !== "diff" && command2 !== "pull" && command2 !== "push")
37705
37839
  throw new Error(
@@ -37827,6 +37961,9 @@ var run = async () => {
37827
37961
  case "data-file":
37828
37962
  await runDataFile(rest);
37829
37963
  return;
37964
+ case "storage":
37965
+ await runStorage(rest);
37966
+ return;
37830
37967
  case "integration":
37831
37968
  await runIntegration(rest);
37832
37969
  return;
@@ -643,31 +643,6 @@ var automationStateSchema = external_exports.record(
643
643
  external_exports.string(),
644
644
  automationNodeStateSchema
645
645
  );
646
- var automationNodePathSchema = external_exports.tuple([
647
- external_exports.array(
648
- external_exports.tuple([
649
- external_exports.string().describe("The variableId of the loop node"),
650
- external_exports.number().min(0).describe("The iteration number of the loop node")
651
- ])
652
- ).describe(
653
- "An array of loop variableIds and iterations, ordered from outermost to innermost"
654
- ),
655
- external_exports.string().describe(
656
- "The variableId of the terminal (innermost) node in the automation"
657
- )
658
- ]);
659
- var emptyAutomationNodePath = [[], ""];
660
- var addAutomationNodePathLoop = (currentPath, iteration) => {
661
- const result = [
662
- [...currentPath[0], [currentPath[1], iteration]],
663
- ""
664
- ];
665
- return result;
666
- };
667
- var setAutomationNodePathTerminal = (currentPath, node) => {
668
- const result = [[...currentPath[0]], node];
669
- return result;
670
- };
671
646
  var runStepPayloadSchema = external_exports.object({
672
647
  runId: external_exports.string(),
673
648
  automationId: external_exports.string(),
@@ -679,16 +654,13 @@ export {
679
654
  MacAddressComparatorOptions,
680
655
  NodeEnum,
681
656
  ServiceEnum,
682
- addAutomationNodePathLoop,
683
657
  automationFileTypesSchema,
684
- automationNodePathSchema,
685
658
  automationNodeSchema,
686
659
  automationNodeStateSchema,
687
660
  automationNodesSchema,
688
661
  automationStateSchema,
689
662
  conditionalNodeTypeIds,
690
663
  doesNodeRequireExecutionContext,
691
- emptyAutomationNodePath,
692
664
  httpMethodSchema,
693
665
  integrationHttpActionEditableSchema,
694
666
  integrationHttpActionOutputs,
@@ -701,7 +673,6 @@ export {
701
673
  jsonStringSchema,
702
674
  monthsSchema,
703
675
  runStepPayloadSchema,
704
- setAutomationNodePathTerminal,
705
676
  timeZoneOffset,
706
677
  timeZonesSchema,
707
678
  transformationSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overaihq/cli",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "Create, edit, deploy, and run OverAI workspace resources from the command line",
5
5
  "type": "module",
6
6
  "homepage": "https://overai.com",
@@ -21,12 +21,12 @@
21
21
  },
22
22
  "devDependencies": {
23
23
  "@perygee/common": "0.0.0",
24
- "@types/node": "^24.13.3",
24
+ "@types/node": "^24.13.5",
25
25
  "@types/proper-lockfile": "^4.1.4",
26
26
  "tsup": "^8.5.1",
27
27
  "typescript": "^7.0.2",
28
28
  "vitest": "^4.1.11",
29
- "zod": "^4.5.4"
29
+ "zod": "^4.6.5"
30
30
  },
31
31
  "dependencies": {
32
32
  "proper-lockfile": "^4.1.2"