@overaihq/cli 0.7.0 → 0.8.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.
@@ -362,6 +362,14 @@ var consoleLogsPayloadSchema = external_exports.object({
362
362
  var runtimeErrorPayloadSchema = external_exports.object({
363
363
  error: external_exports.string().nullable()
364
364
  });
365
+ var pageInteractionPayloadSchema = external_exports.object({
366
+ clicks: external_exports.number().int().min(0),
367
+ keypresses: external_exports.number().int().min(0)
368
+ });
369
+ var pageErrorPayloadSchema = external_exports.object({
370
+ message: external_exports.string(),
371
+ stack: external_exports.string().optional()
372
+ });
365
373
 
366
374
  // ../common/src/webPage.ts
367
375
  var webPageLanguage = ["react"];
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  itemFilterExpressionRootSchema,
7
7
  webPageConfigSchema
8
- } from "./chunk-XGGDXRQI.js";
8
+ } from "./chunk-2F4ZHHFG.js";
9
9
  import {
10
10
  external_exports
11
11
  } from "./chunk-6YTIPJTD.js";
@@ -26360,11 +26360,6 @@ var require_lib2 = __commonJS({
26360
26360
  }
26361
26361
  });
26362
26362
 
26363
- // src/auth.ts
26364
- import { randomBytes as randomBytes2 } from "crypto";
26365
- import { createServer } from "http";
26366
- import { hostname } from "os";
26367
-
26368
26363
  // src/config.ts
26369
26364
  import {
26370
26365
  chmodSync,
@@ -26401,6 +26396,7 @@ var clearConfig = () => {
26401
26396
  var trimSlash = (url) => url.replace(/\/+$/, "");
26402
26397
  var DEFAULT_API_BASE_URL = "https://api.overai.com";
26403
26398
  var apiBaseUrlOverride = process.env.OVERAI_API_URL != null ? trimSlash(process.env.OVERAI_API_URL) : void 0;
26399
+ var orgIdOverride = process.env.OVERAI_ORG_ID != null && process.env.OVERAI_ORG_ID !== "" ? process.env.OVERAI_ORG_ID : void 0;
26404
26400
  var appBaseUrl = trimSlash(
26405
26401
  process.env.OVERAI_APP_URL ?? "https://app.overai.com"
26406
26402
  );
@@ -26449,6 +26445,21 @@ var orgScopeOverride = null;
26449
26445
  var setOrgScope = (organizationId) => {
26450
26446
  orgScopeOverride = organizationId;
26451
26447
  };
26448
+ var resolveOrgId = (config) => orgIdOverride ?? orgScopeOverride ?? config.organizationId;
26449
+ var scopedOrgId = () => {
26450
+ const config = readConfig();
26451
+ return config == null ? null : resolveOrgId(config);
26452
+ };
26453
+ var announceOrgScope = () => {
26454
+ const config = readConfig();
26455
+ if (config == null) return;
26456
+ const targetOrgId = resolveOrgId(config);
26457
+ if (targetOrgId === config.organizationId) return;
26458
+ process.stderr.write(
26459
+ `Using org ${targetOrgId} (${orgIdOverride != null ? "OVERAI_ORG_ID" : ".overai/manifest.json"}); the active org is ${config.organizationId}.
26460
+ `
26461
+ );
26462
+ };
26452
26463
  var inFlightExchange = null;
26453
26464
  var processToken = null;
26454
26465
  var getAccessToken = async () => {
@@ -26456,7 +26467,7 @@ var getAccessToken = async () => {
26456
26467
  if (config == null) {
26457
26468
  throw new Error("Not signed in. Run `overai signin`.");
26458
26469
  }
26459
- const targetOrgId = orgScopeOverride ?? config.organizationId;
26470
+ const targetOrgId = resolveOrgId(config);
26460
26471
  const skewMs = 6e4;
26461
26472
  if (targetOrgId === config.organizationId && config.accessToken != null && config.accessTokenExpiresAt != null && config.accessTokenExpiresAt - skewMs > Date.now()) {
26462
26473
  return { token: config.accessToken, organizationId: config.organizationId };
@@ -26498,6 +26509,45 @@ var v2Fetch = async (path, init) => {
26498
26509
  headers.set("Authorization", `Bearer ${auth.token}`);
26499
26510
  return fetch(`${apiBaseUrl()}${path}`, { ...init, headers });
26500
26511
  };
26512
+ var STATUS_TEXT = {
26513
+ 400: "Bad Request",
26514
+ 401: "Unauthorized",
26515
+ 403: "Forbidden",
26516
+ 404: "Not Found",
26517
+ 409: "Conflict",
26518
+ 413: "Payload Too Large",
26519
+ 429: "Too Many Requests",
26520
+ 500: "Internal Server Error",
26521
+ 503: "Service Unavailable"
26522
+ };
26523
+ var v2Error = (label, res, rawBody) => {
26524
+ const structured = (() => {
26525
+ try {
26526
+ return JSON.parse(rawBody);
26527
+ } catch {
26528
+ return null;
26529
+ }
26530
+ })();
26531
+ const field = structured?.error ?? structured?.message;
26532
+ const claimed = (typeof field === "string" ? field : rawBody).trim().slice(0, 500);
26533
+ const reason = claimed.length === 0 || claimed === "Error" || claimed === "null" || claimed === "undefined" || claimed.toLowerCase() === STATUS_TEXT[res.status]?.toLowerCase() ? null : claimed;
26534
+ const orgHint = res.status === 404 || res.status === 403 ? `
26535
+ If it lives in another org, prefix the command with OVERAI_ORG_ID=<orgId> (\`overai org list\` shows them).` : "";
26536
+ if (reason != null) return new Error(`${reason}${orgHint}`);
26537
+ const path = (() => {
26538
+ try {
26539
+ return new URL(res.url).pathname;
26540
+ } catch {
26541
+ return "";
26542
+ }
26543
+ })();
26544
+ const org = scopedOrgId();
26545
+ return new Error(
26546
+ `${label}: HTTP ${String(res.status)}, no reason given by the server.${path === "" ? "" : `
26547
+ ${path}`}${org == null ? "" : `
26548
+ Asked as org ${org}.`}${orgHint}`
26549
+ );
26550
+ };
26501
26551
  var userTrpcMutation = async (procedure, input) => {
26502
26552
  const res = await v2Fetch(`/v1/utils/trpc/${procedure}`, {
26503
26553
  method: "POST",
@@ -26507,6 +26557,11 @@ var userTrpcMutation = async (procedure, input) => {
26507
26557
  return trpcResult(res, procedure);
26508
26558
  };
26509
26559
 
26560
+ // src/auth.ts
26561
+ import { randomBytes as randomBytes2 } from "crypto";
26562
+ import { createServer } from "http";
26563
+ import { hostname } from "os";
26564
+
26510
26565
  // src/browser.ts
26511
26566
  import { spawn } from "child_process";
26512
26567
  import { platform } from "os";
@@ -26521,6 +26576,175 @@ var openBrowser = (url) => {
26521
26576
  }
26522
26577
  };
26523
26578
 
26579
+ // src/args.ts
26580
+ import { readFileSync as readFileSync2 } from "fs";
26581
+ var flagValue = (args, ...names) => {
26582
+ for (const name2 of names) {
26583
+ const i = args.indexOf(name2);
26584
+ if (i !== -1 && i + 1 < args.length) return args[i + 1];
26585
+ }
26586
+ return void 0;
26587
+ };
26588
+ var stdinClaimedBy = null;
26589
+ var readStdin = async (claimant = "stdin") => {
26590
+ if (stdinClaimedBy != null) {
26591
+ throw new Error(
26592
+ `Only one value can be read from stdin; ${stdinClaimedBy} already consumed it.`
26593
+ );
26594
+ }
26595
+ stdinClaimedBy = claimant;
26596
+ const chunks = [];
26597
+ for await (const chunk of process.stdin) {
26598
+ chunks.push(chunk);
26599
+ }
26600
+ return Buffer.concat(chunks).toString("utf8");
26601
+ };
26602
+ var readValue = async (value, label) => {
26603
+ if (value?.startsWith("@") !== true) return value;
26604
+ if (value.startsWith("@@")) return value.slice(1);
26605
+ const path = value.slice(1);
26606
+ if (path === "-") return (await readStdin(`${label} @-`)).trim();
26607
+ try {
26608
+ return readFileSync2(path, "utf8").trim();
26609
+ } catch {
26610
+ throw new Error(
26611
+ `${label}: could not read file "${path}". Write "@@" to send a literal value starting with "@".`
26612
+ );
26613
+ }
26614
+ };
26615
+
26616
+ // src/output.ts
26617
+ var resolveFormat = (explicit) => {
26618
+ if (explicit === "json" || explicit === "csv" || explicit === "table") {
26619
+ return explicit;
26620
+ }
26621
+ if (explicit != null) {
26622
+ throw new Error(
26623
+ `Unknown output format "${explicit}". Use json, table, or csv.`
26624
+ );
26625
+ }
26626
+ return process.stdout.isTTY ? "table" : "json";
26627
+ };
26628
+ var cell = (value) => {
26629
+ if (value == null) return "";
26630
+ if (typeof value === "string") return value;
26631
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
26632
+ return String(value);
26633
+ }
26634
+ return JSON.stringify(value);
26635
+ };
26636
+ var formatResult = (data, format2) => {
26637
+ if (format2 === "json" || !Array.isArray(data)) {
26638
+ return JSON.stringify(data, null, 2);
26639
+ }
26640
+ const records = data.map(
26641
+ (row) => row != null && typeof row === "object" ? row : { value: row }
26642
+ );
26643
+ if (records.length === 0) return "(no rows)";
26644
+ const columns = [...new Set(records.flatMap((r) => Object.keys(r)))];
26645
+ if (format2 === "csv") {
26646
+ const escape2 = (value) => {
26647
+ const text = cell(value);
26648
+ return /[",\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
26649
+ };
26650
+ return [
26651
+ columns.join(","),
26652
+ ...records.map((r) => columns.map((c) => escape2(r[c])).join(","))
26653
+ ].join("\n");
26654
+ }
26655
+ const widths = columns.map(
26656
+ (c) => Math.max(c.length, ...records.map((r) => cell(r[c]).length))
26657
+ );
26658
+ const line = (cells) => cells.map((text, i) => text.padEnd(widths[i] ?? 0)).join(" ");
26659
+ return [
26660
+ line(columns),
26661
+ line(widths.map((w) => "-".repeat(w))),
26662
+ ...records.map((r) => line(columns.map((c) => cell(r[c]))))
26663
+ ].join("\n");
26664
+ };
26665
+ var printResult = (data, format2) => {
26666
+ process.stdout.write(`${formatResult(data, format2)}
26667
+ `);
26668
+ };
26669
+
26670
+ // src/commands/org.ts
26671
+ var USER_ORGS_QUERY = `
26672
+ query UserOrganizations($identityId: String!) {
26673
+ identityOrganizations(
26674
+ where: { identityId: { _eq: $identityId }, disabled: { _eq: false } }
26675
+ ) {
26676
+ organization { id name }
26677
+ }
26678
+ }
26679
+ `;
26680
+ var fetchOrgNames = async () => {
26681
+ const orgs = await listOrgRows();
26682
+ return new Map(orgs.map((org) => [org.id, org.name ?? ""]));
26683
+ };
26684
+ var listOrgRows = async () => {
26685
+ const auth = await getAccessToken();
26686
+ const identityId = decodeJwtClaims(auth.token).user_id;
26687
+ if (identityId == null) {
26688
+ throw new Error("Could not read identity from the current session.");
26689
+ }
26690
+ const res = await v2Fetch("/v1/graphql", {
26691
+ method: "POST",
26692
+ headers: { "Content-Type": "application/json" },
26693
+ body: JSON.stringify({ query: USER_ORGS_QUERY, variables: { identityId } })
26694
+ });
26695
+ const body = await res.json().catch(() => null);
26696
+ if (body?.errors != null && body.errors.length > 0) {
26697
+ throw new Error(body.errors.map((e) => e.message).join("; "));
26698
+ }
26699
+ if (!res.ok || body == null) {
26700
+ throw new Error(`org list failed (${String(res.status)})`);
26701
+ }
26702
+ return (body.data?.identityOrganizations ?? []).map((edge) => edge.organization).filter((org) => org != null);
26703
+ };
26704
+ var listOrgs = async (args) => {
26705
+ const active = scopedOrgId();
26706
+ printResult(
26707
+ (await listOrgRows()).map((org) => ({
26708
+ id: org.id,
26709
+ name: org.name ?? "",
26710
+ active: org.id === active
26711
+ })),
26712
+ resolveFormat(flagValue(args, "-o", "--output"))
26713
+ );
26714
+ };
26715
+ var switchOrg = async (orgId) => {
26716
+ const config = readConfig();
26717
+ if (config == null) {
26718
+ process.stderr.write("Not signed in. Run `overai signin`.\n");
26719
+ process.exitCode = 1;
26720
+ return;
26721
+ }
26722
+ const result = await exchangeSessionToken(config.refreshToken, orgId);
26723
+ if (result.type === "redirect") {
26724
+ process.stdout.write(
26725
+ `This organization requires SSO. Complete sign-in in your browser, then run \`overai signin\`.
26726
+ ${result.redirectUrl}
26727
+ `
26728
+ );
26729
+ openBrowser(result.redirectUrl);
26730
+ process.exitCode = 1;
26731
+ return;
26732
+ }
26733
+ writeConfig({
26734
+ ...config,
26735
+ organizationId: orgId,
26736
+ email: decodeJwtClaims(result.token).email ?? config.email,
26737
+ accessToken: result.token,
26738
+ accessTokenExpiresAt: Date.now() + result.accessTokenValiditySeconds * 1e3,
26739
+ refreshToken: result.refreshToken
26740
+ });
26741
+ process.stdout.write(
26742
+ `Switched to org ${orgId}.
26743
+ This is machine-global \u2014 every shell and editor session on this machine now uses it. Prefix a command with OVERAI_ORG_ID=<orgId> to scope just one.
26744
+ `
26745
+ );
26746
+ };
26747
+
26524
26748
  // src/pkce.ts
26525
26749
  import { createHash, randomBytes } from "crypto";
26526
26750
  var createPkcePair = () => {
@@ -26529,6 +26753,87 @@ var createPkcePair = () => {
26529
26753
  return { verifier, challenge };
26530
26754
  };
26531
26755
 
26756
+ // src/workspace/manifest.ts
26757
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
26758
+ import { dirname, join as join2, resolve } from "path";
26759
+ var MANIFEST_DIR = ".overai";
26760
+ var MANIFEST_FILE = "manifest.json";
26761
+ var BASE_DIR = "base";
26762
+ var findWorkspaceRoot = (from = process.cwd()) => {
26763
+ let dir = resolve(from);
26764
+ for (; ; ) {
26765
+ if (existsSync(join2(dir, MANIFEST_DIR, MANIFEST_FILE))) return dir;
26766
+ const parent = dirname(dir);
26767
+ if (parent === dir) {
26768
+ throw new Error(
26769
+ "Not inside a cloned workspace (no .overai/manifest.json found). Run `overai clone <workspaceId>` first."
26770
+ );
26771
+ }
26772
+ dir = parent;
26773
+ }
26774
+ };
26775
+ var readManifest = (root) => JSON.parse(
26776
+ readFileSync3(join2(root, MANIFEST_DIR, MANIFEST_FILE), "utf8")
26777
+ );
26778
+ var openClone = () => {
26779
+ const root = findWorkspaceRoot();
26780
+ const manifest = readManifest(root);
26781
+ setOrgScope(manifest.organizationId);
26782
+ return { root, manifest };
26783
+ };
26784
+ var pinOrgFromClone = () => {
26785
+ try {
26786
+ setOrgScope(readManifest(findWorkspaceRoot()).organizationId);
26787
+ } catch {
26788
+ }
26789
+ };
26790
+ var writeManifest = (root, manifest) => {
26791
+ mkdirSync2(join2(root, MANIFEST_DIR), { recursive: true });
26792
+ writeFileSync2(
26793
+ join2(root, MANIFEST_DIR, MANIFEST_FILE),
26794
+ `${JSON.stringify(manifest, null, 2)}
26795
+ `
26796
+ );
26797
+ };
26798
+ var basePath = (root, relPath) => join2(root, MANIFEST_DIR, BASE_DIR, relPath);
26799
+ var writeSynced = (root, relPath, content) => {
26800
+ for (const abs2 of [join2(root, relPath), basePath(root, relPath)]) {
26801
+ mkdirSync2(dirname(abs2), { recursive: true });
26802
+ writeFileSync2(abs2, content);
26803
+ }
26804
+ };
26805
+ var readWorkingFile = (root, relPath) => {
26806
+ try {
26807
+ return readFileSync3(join2(root, relPath), "utf8");
26808
+ } catch {
26809
+ return null;
26810
+ }
26811
+ };
26812
+ var readBaseFile = (root, relPath) => {
26813
+ try {
26814
+ return readFileSync3(basePath(root, relPath), "utf8");
26815
+ } catch {
26816
+ return null;
26817
+ }
26818
+ };
26819
+ var slugify = (name2) => name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
26820
+ var localState = (root, resource) => {
26821
+ const changedFiles = [];
26822
+ let missing = false;
26823
+ for (const relPath of resource.files) {
26824
+ const working = readWorkingFile(root, relPath);
26825
+ if (working == null) {
26826
+ missing = true;
26827
+ continue;
26828
+ }
26829
+ if (working !== readBaseFile(root, relPath)) changedFiles.push(relPath);
26830
+ }
26831
+ return {
26832
+ state: missing ? "missing" : changedFiles.length > 0 ? "modified" : "unchanged",
26833
+ changedFiles
26834
+ };
26835
+ };
26836
+
26532
26837
  // src/auth.ts
26533
26838
  var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
26534
26839
  var startCallbackServer = (expectedState) => {
@@ -26640,108 +26945,33 @@ var signOut = async () => {
26640
26945
  clearConfig();
26641
26946
  process.stdout.write("Signed out.\n");
26642
26947
  };
26643
- var whoami = () => {
26948
+ var whoami = async () => {
26644
26949
  const config = readConfig();
26645
26950
  if (config == null) {
26646
26951
  process.stderr.write("Not signed in. Run `overai signin`.\n");
26647
26952
  process.exitCode = 1;
26648
26953
  return;
26649
26954
  }
26955
+ const cloneOrgId = (() => {
26956
+ try {
26957
+ return readManifest(findWorkspaceRoot()).organizationId;
26958
+ } catch {
26959
+ return null;
26960
+ }
26961
+ })();
26962
+ const effective = orgIdOverride ?? cloneOrgId ?? config.organizationId;
26963
+ const names = await fetchOrgNames().catch(() => null);
26964
+ const label = (id) => names?.get(id) == null ? id : `${id} \u2014 ${String(names.get(id))}`;
26650
26965
  process.stdout.write(
26651
- `Signed in${config.email != null ? ` as ${config.email}` : ""} (org ${config.organizationId}).
26966
+ `Signed in${config.email != null ? ` as ${config.email}` : ""} (org ${label(effective)}).
26652
26967
  `
26653
26968
  );
26654
- };
26655
-
26656
- // src/args.ts
26657
- import { readFileSync as readFileSync2 } from "fs";
26658
- var flagValue = (args, ...names) => {
26659
- for (const name2 of names) {
26660
- const i = args.indexOf(name2);
26661
- if (i !== -1 && i + 1 < args.length) return args[i + 1];
26662
- }
26663
- return void 0;
26664
- };
26665
- var stdinClaimedBy = null;
26666
- var readStdin = async (claimant = "stdin") => {
26667
- if (stdinClaimedBy != null) {
26668
- throw new Error(
26669
- `Only one value can be read from stdin; ${stdinClaimedBy} already consumed it.`
26670
- );
26671
- }
26672
- stdinClaimedBy = claimant;
26673
- const chunks = [];
26674
- for await (const chunk of process.stdin) {
26675
- chunks.push(chunk);
26676
- }
26677
- return Buffer.concat(chunks).toString("utf8");
26678
- };
26679
- var readValue = async (value, label) => {
26680
- if (value?.startsWith("@") !== true) return value;
26681
- if (value.startsWith("@@")) return value.slice(1);
26682
- const path = value.slice(1);
26683
- if (path === "-") return (await readStdin(`${label} @-`)).trim();
26684
- try {
26685
- return readFileSync2(path, "utf8").trim();
26686
- } catch {
26687
- throw new Error(
26688
- `${label}: could not read file "${path}". Write "@@" to send a literal value starting with "@".`
26689
- );
26690
- }
26691
- };
26692
-
26693
- // src/output.ts
26694
- var resolveFormat = (explicit) => {
26695
- if (explicit === "json" || explicit === "csv" || explicit === "table") {
26696
- return explicit;
26697
- }
26698
- if (explicit != null) {
26699
- throw new Error(
26700
- `Unknown output format "${explicit}". Use json, table, or csv.`
26969
+ if (effective !== config.organizationId) {
26970
+ process.stdout.write(
26971
+ `Scoped by ${orgIdOverride != null ? "OVERAI_ORG_ID" : ".overai/manifest.json"}; the machine-global active org is ${label(config.organizationId)}.
26972
+ `
26701
26973
  );
26702
26974
  }
26703
- return process.stdout.isTTY ? "table" : "json";
26704
- };
26705
- var cell = (value) => {
26706
- if (value == null) return "";
26707
- if (typeof value === "string") return value;
26708
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
26709
- return String(value);
26710
- }
26711
- return JSON.stringify(value);
26712
- };
26713
- var formatResult = (data, format2) => {
26714
- if (format2 === "json" || !Array.isArray(data)) {
26715
- return JSON.stringify(data, null, 2);
26716
- }
26717
- const records = data.map(
26718
- (row) => row != null && typeof row === "object" ? row : { value: row }
26719
- );
26720
- if (records.length === 0) return "(no rows)";
26721
- const columns = [...new Set(records.flatMap((r) => Object.keys(r)))];
26722
- if (format2 === "csv") {
26723
- const escape2 = (value) => {
26724
- const text = cell(value);
26725
- return /[",\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
26726
- };
26727
- return [
26728
- columns.join(","),
26729
- ...records.map((r) => columns.map((c) => escape2(r[c])).join(","))
26730
- ].join("\n");
26731
- }
26732
- const widths = columns.map(
26733
- (c) => Math.max(c.length, ...records.map((r) => cell(r[c]).length))
26734
- );
26735
- const line = (cells) => cells.map((text, i) => text.padEnd(widths[i] ?? 0)).join(" ");
26736
- return [
26737
- line(columns),
26738
- line(widths.map((w) => "-".repeat(w))),
26739
- ...records.map((r) => line(columns.map((c) => cell(r[c]))))
26740
- ].join("\n");
26741
- };
26742
- var printResult = (data, format2) => {
26743
- process.stdout.write(`${formatResult(data, format2)}
26744
- `);
26745
26975
  };
26746
26976
 
26747
26977
  // src/commands/agent.ts
@@ -26756,8 +26986,9 @@ var runAgent = async (args) => {
26756
26986
  headers: { "Content-Type": "application/json" },
26757
26987
  body: JSON.stringify({ message })
26758
26988
  });
26759
- const body = await response.json().catch(() => null);
26760
- if (!response.ok) throw new Error(body == null ? `Agent generate failed (${String(response.status)}).` : JSON.stringify(body));
26989
+ const raw2 = await response.text();
26990
+ if (!response.ok) throw v2Error("agent generate", response, raw2);
26991
+ const body = JSON.parse(raw2);
26761
26992
  printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
26762
26993
  };
26763
26994
 
@@ -26771,12 +27002,20 @@ var gqlRequest = async (query, variables, options) => {
26771
27002
  body: JSON.stringify({ query, variables })
26772
27003
  }
26773
27004
  );
26774
- const body = await res.json().catch(() => null);
27005
+ const raw2 = await res.text();
27006
+ const body = (() => {
27007
+ try {
27008
+ return JSON.parse(raw2);
27009
+ } catch {
27010
+ return null;
27011
+ }
27012
+ })();
26775
27013
  if (body?.errors != null && body.errors.length > 0) {
26776
27014
  throw new Error(body.errors.map((e) => e.message).join("; "));
26777
27015
  }
26778
- if (!res.ok || body?.data == null) {
26779
- throw new Error(`graphql failed (${String(res.status)})`);
27016
+ if (!res.ok) throw v2Error("graphql", res, raw2);
27017
+ if (body?.data == null) {
27018
+ throw new Error("graphql returned no data and no errors.");
26780
27019
  }
26781
27020
  return body.data;
26782
27021
  };
@@ -26852,12 +27091,9 @@ var runTrigger = async (automationId, args) => {
26852
27091
  body: JSON.stringify(body)
26853
27092
  }
26854
27093
  );
26855
- const responseBody = await res.json().catch(() => null);
26856
- if (!res.ok) {
26857
- throw new Error(
26858
- responseBody?.error ?? `run failed (${String(res.status)})`
26859
- );
26860
- }
27094
+ const raw2 = await res.text();
27095
+ if (!res.ok) throw v2Error("automation run", res, raw2);
27096
+ const responseBody = JSON.parse(raw2);
26861
27097
  printResult(responseBody, resolveFormat(flagValue(args, "-o", "--output")));
26862
27098
  };
26863
27099
  var runAutomation = async (args) => {
@@ -26871,81 +27107,6 @@ var runAutomation = async (args) => {
26871
27107
  process.exitCode = 1;
26872
27108
  };
26873
27109
 
26874
- // src/workspace/manifest.ts
26875
- import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
26876
- import { dirname, join as join2, resolve } from "path";
26877
- var MANIFEST_DIR = ".overai";
26878
- var MANIFEST_FILE = "manifest.json";
26879
- var BASE_DIR = "base";
26880
- var findWorkspaceRoot = (from = process.cwd()) => {
26881
- let dir = resolve(from);
26882
- for (; ; ) {
26883
- if (existsSync(join2(dir, MANIFEST_DIR, MANIFEST_FILE))) return dir;
26884
- const parent = dirname(dir);
26885
- if (parent === dir) {
26886
- throw new Error(
26887
- "Not inside a cloned workspace (no .overai/manifest.json found). Run `overai clone <workspaceId>` first."
26888
- );
26889
- }
26890
- dir = parent;
26891
- }
26892
- };
26893
- var readManifest = (root) => JSON.parse(
26894
- readFileSync3(join2(root, MANIFEST_DIR, MANIFEST_FILE), "utf8")
26895
- );
26896
- var openClone = () => {
26897
- const root = findWorkspaceRoot();
26898
- const manifest = readManifest(root);
26899
- setOrgScope(manifest.organizationId);
26900
- return { root, manifest };
26901
- };
26902
- var writeManifest = (root, manifest) => {
26903
- mkdirSync2(join2(root, MANIFEST_DIR), { recursive: true });
26904
- writeFileSync2(
26905
- join2(root, MANIFEST_DIR, MANIFEST_FILE),
26906
- `${JSON.stringify(manifest, null, 2)}
26907
- `
26908
- );
26909
- };
26910
- var basePath = (root, relPath) => join2(root, MANIFEST_DIR, BASE_DIR, relPath);
26911
- var writeSynced = (root, relPath, content) => {
26912
- for (const abs2 of [join2(root, relPath), basePath(root, relPath)]) {
26913
- mkdirSync2(dirname(abs2), { recursive: true });
26914
- writeFileSync2(abs2, content);
26915
- }
26916
- };
26917
- var readWorkingFile = (root, relPath) => {
26918
- try {
26919
- return readFileSync3(join2(root, relPath), "utf8");
26920
- } catch {
26921
- return null;
26922
- }
26923
- };
26924
- var readBaseFile = (root, relPath) => {
26925
- try {
26926
- return readFileSync3(basePath(root, relPath), "utf8");
26927
- } catch {
26928
- return null;
26929
- }
26930
- };
26931
- var slugify = (name2) => name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
26932
- var localState = (root, resource) => {
26933
- const changedFiles = [];
26934
- let missing = false;
26935
- for (const relPath of resource.files) {
26936
- const working = readWorkingFile(root, relPath);
26937
- if (working == null) {
26938
- missing = true;
26939
- continue;
26940
- }
26941
- if (working !== readBaseFile(root, relPath)) changedFiles.push(relPath);
26942
- }
26943
- return {
26944
- state: missing ? "missing" : changedFiles.length > 0 ? "modified" : "unchanged",
26945
- changedFiles
26946
- };
26947
- };
26948
-
26949
27110
  // src/workspace/validateElements.ts
26950
27111
  var findings = (parsed) => parsed.success ? [] : parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
26951
27112
  var validateAutomationElements = async (elements) => {
@@ -26953,7 +27114,7 @@ var validateAutomationElements = async (elements) => {
26953
27114
  return findings(automationNodesSchema.safeParse(elements));
26954
27115
  };
26955
27116
  var validateWebPageConfig = async (config) => {
26956
- const { webPageConfigWriteSchema } = await import("./webPage-UHT5X2WK.js");
27117
+ const { webPageConfigWriteSchema } = await import("./webPage-G5HIZM7Q.js");
26957
27118
  return findings(webPageConfigWriteSchema.safeParse(config));
26958
27119
  };
26959
27120
 
@@ -27553,10 +27714,9 @@ var enqueueAction = async (runnerId, actionId, inputs, args) => {
27553
27714
  body: JSON.stringify({ actionId, inputs, actionRunnerId: runnerId })
27554
27715
  }
27555
27716
  );
27556
- const body = await res.json().catch(() => null);
27557
- if (!res.ok) {
27558
- throw new Error(body?.error ?? `run failed (${String(res.status)})`);
27559
- }
27717
+ const raw2 = await res.text();
27718
+ if (!res.ok) throw v2Error("runner run", res, raw2);
27719
+ const body = JSON.parse(raw2);
27560
27720
  printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
27561
27721
  };
27562
27722
  var runRun = async (runnerId, args) => {
@@ -33361,14 +33521,14 @@ var runDataFile = async (args) => {
33361
33521
  message: flagValue(args, "-m", "--message") ?? ""
33362
33522
  });
33363
33523
  const response2 = await fetch(url, { method: "PUT", body: readFileSync5(path) });
33364
- if (!response2.ok) throw new Error(`Upload failed (${String(response2.status)}): ${await response2.text()}`);
33524
+ if (!response2.ok) throw v2Error("data-file upload", response2, await response2.text());
33365
33525
  process.stdout.write(`${id}
33366
33526
  `);
33367
33527
  return;
33368
33528
  }
33369
33529
  const suffix = operation === "head" ? `/head?numLines=${encodeURIComponent(flagValue(args, "--lines") ?? "50")}` : "";
33370
33530
  const response = await v2Fetch(`/v2/data-files/${organizationId}/${id}${suffix}`, { redirect: "follow" });
33371
- if (!response.ok) throw new Error(`Data file ${String(operation)} failed (${String(response.status)}): ${await response.text()}`);
33531
+ if (!response.ok) throw v2Error(`data-file ${String(operation)}`, response, await response.text());
33372
33532
  const bytes = Buffer.from(await response.arrayBuffer());
33373
33533
  if (path != null && path !== "-") writeFileSync3(path, bytes);
33374
33534
  else process.stdout.write(bytes);
@@ -33382,11 +33542,11 @@ var runDataStream = async (args) => {
33382
33542
  }
33383
33543
  const { organizationId } = await getAccessToken();
33384
33544
  if (operation === "append") {
33385
- const raw2 = await readValue(args[2], "data stream input");
33386
- if (raw2 == null) throw new Error("Data stream append requires JSON, @<file>, or @-.");
33545
+ const raw3 = await readValue(args[2], "data stream input");
33546
+ if (raw3 == null) throw new Error("Data stream append requires JSON, @<file>, or @-.");
33387
33547
  const parsed = (() => {
33388
33548
  try {
33389
- return JSON.parse(raw2);
33549
+ return JSON.parse(raw3);
33390
33550
  } catch {
33391
33551
  throw new Error("Data stream input must be valid JSON.");
33392
33552
  }
@@ -33396,8 +33556,9 @@ var runDataStream = async (args) => {
33396
33556
  headers: { "Content-Type": "application/json" },
33397
33557
  body: JSON.stringify({ data: Array.isArray(parsed) ? parsed : [parsed] })
33398
33558
  });
33399
- const body2 = await response2.json().catch(() => null);
33400
- if (!response2.ok) throw new Error(JSON.stringify(body2));
33559
+ const appended = await response2.text();
33560
+ if (!response2.ok) throw v2Error("data-stream append", response2, appended);
33561
+ const body2 = JSON.parse(appended);
33401
33562
  printResult(body2, resolveFormat(flagValue(args, "-o", "--output")));
33402
33563
  return;
33403
33564
  }
@@ -33408,8 +33569,9 @@ var runDataStream = async (args) => {
33408
33569
  const limit2 = flagValue(args, "--limit");
33409
33570
  if (limit2 != null) query.set("limit", limit2);
33410
33571
  const response = await v2Fetch(`/v1/data-streams/${organizationId}/${id}?${query.toString()}`);
33411
- const body = await response.json().catch(() => null);
33412
- if (!response.ok) throw new Error(JSON.stringify(body));
33572
+ const raw2 = await response.text();
33573
+ if (!response.ok) throw v2Error("data-stream query", response, raw2);
33574
+ const body = JSON.parse(raw2);
33413
33575
  printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
33414
33576
  };
33415
33577
 
@@ -34112,18 +34274,7 @@ var runFn = async (args) => {
34112
34274
  `);
34113
34275
  }
34114
34276
  const body = await res.text();
34115
- if (!res.ok) {
34116
- const structured = (() => {
34117
- try {
34118
- return JSON.parse(body);
34119
- } catch {
34120
- return null;
34121
- }
34122
- })();
34123
- throw new Error(
34124
- structured?.error ?? (body.trim() || `fn failed (${String(res.status)})`)
34125
- );
34126
- }
34277
+ if (!res.ok) throw v2Error("fn", res, body);
34127
34278
  process.stdout.write(`${body}
34128
34279
  `);
34129
34280
  };
@@ -34165,12 +34316,20 @@ var runGraphql = async (args) => {
34165
34316
  body: JSON.stringify({ query, variables })
34166
34317
  }
34167
34318
  );
34168
- const body = await res.json().catch(() => null);
34319
+ const raw2 = await res.text();
34320
+ const body = (() => {
34321
+ try {
34322
+ return JSON.parse(raw2);
34323
+ } catch {
34324
+ return null;
34325
+ }
34326
+ })();
34169
34327
  if (body?.errors != null && body.errors.length > 0) {
34170
34328
  throw new Error(body.errors.map((e) => e.message).join("; "));
34171
34329
  }
34172
- if (!res.ok || body == null) {
34173
- throw new Error(`graphql failed (${String(res.status)})`);
34330
+ if (!res.ok) throw v2Error("graphql", res, raw2);
34331
+ if (body == null) {
34332
+ throw new Error("graphql returned an unreadable body.");
34174
34333
  }
34175
34334
  printResult(body.data, resolveFormat(flagValue(args, "-o", "--output")));
34176
34335
  };
@@ -34262,71 +34421,6 @@ var runOpen = async (args) => {
34262
34421
  openBrowser(url);
34263
34422
  };
34264
34423
 
34265
- // src/commands/org.ts
34266
- var USER_ORGS_QUERY = `
34267
- query UserOrganizations($identityId: String!) {
34268
- identityOrganizations(
34269
- where: { identityId: { _eq: $identityId }, disabled: { _eq: false } }
34270
- ) {
34271
- organization { id name }
34272
- }
34273
- }
34274
- `;
34275
- var listOrgs = async (args) => {
34276
- const auth = await getAccessToken();
34277
- const identityId = decodeJwtClaims(auth.token).user_id;
34278
- if (identityId == null) {
34279
- throw new Error("Could not read identity from the current session.");
34280
- }
34281
- const res = await v2Fetch("/v1/graphql", {
34282
- method: "POST",
34283
- headers: { "Content-Type": "application/json" },
34284
- body: JSON.stringify({ query: USER_ORGS_QUERY, variables: { identityId } })
34285
- });
34286
- const body = await res.json().catch(() => null);
34287
- if (body?.errors != null && body.errors.length > 0) {
34288
- throw new Error(body.errors.map((e) => e.message).join("; "));
34289
- }
34290
- if (!res.ok || body == null) {
34291
- throw new Error(`org list failed (${String(res.status)})`);
34292
- }
34293
- const orgs = (body.data?.identityOrganizations ?? []).map((edge) => edge.organization).filter((org) => org != null).map((org) => ({
34294
- id: org.id,
34295
- name: org.name ?? "",
34296
- active: org.id === auth.organizationId
34297
- }));
34298
- printResult(orgs, resolveFormat(flagValue(args, "-o", "--output")));
34299
- };
34300
- var switchOrg = async (orgId) => {
34301
- const config = readConfig();
34302
- if (config == null) {
34303
- process.stderr.write("Not signed in. Run `overai signin`.\n");
34304
- process.exitCode = 1;
34305
- return;
34306
- }
34307
- const result = await exchangeSessionToken(config.refreshToken, orgId);
34308
- if (result.type === "redirect") {
34309
- process.stdout.write(
34310
- `This organization requires SSO. Complete sign-in in your browser, then run \`overai signin\`.
34311
- ${result.redirectUrl}
34312
- `
34313
- );
34314
- openBrowser(result.redirectUrl);
34315
- process.exitCode = 1;
34316
- return;
34317
- }
34318
- writeConfig({
34319
- ...config,
34320
- organizationId: orgId,
34321
- email: decodeJwtClaims(result.token).email ?? config.email,
34322
- accessToken: result.token,
34323
- accessTokenExpiresAt: Date.now() + result.accessTokenValiditySeconds * 1e3,
34324
- refreshToken: result.refreshToken
34325
- });
34326
- process.stdout.write(`Switched to org ${orgId}.
34327
- `);
34328
- };
34329
-
34330
34424
  // src/commands/printablePage.ts
34331
34425
  var runPrintablePage = async (args) => {
34332
34426
  const [operation, id] = args;
@@ -34346,8 +34440,9 @@ var runPrintablePage = async (args) => {
34346
34440
  body: JSON.stringify({ actionId: "PRINTABLE_PAGE_TO_PDF", inputs: { printablePageId: id, searchParams, includeDiagnostics: true } }),
34347
34441
  signal: AbortSignal.timeout(timeoutMs)
34348
34442
  });
34349
- const body = await response.json().catch(() => null);
34350
- if (!response.ok) throw new Error(JSON.stringify(body));
34443
+ const raw2 = await response.text();
34444
+ if (!response.ok) throw v2Error("printable-page render", response, raw2);
34445
+ const body = JSON.parse(raw2);
34351
34446
  printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
34352
34447
  };
34353
34448
 
@@ -34609,7 +34704,8 @@ deploy\` makes it live. Pick primitives by trigger:
34609
34704
  - Awaitable server-side logic, callable from pages/automations/agents \u2192 a
34610
34705
  **Function** (Deno sandbox, can import from esm.sh, full env SDK).
34611
34706
  - Storage: **TableSpaces** (Postgres, via env.TBSxxx) for structured data;
34612
- **Data Files** (env.DFxxx) for versioned single-file datasets. Prefer
34707
+ **Data Files** (env.DFxxx) for versioned single-file datasets; **File
34708
+ Buckets** (env.FBxxx) for browsable file collections and SFTP. Prefer
34613
34709
  first-class platform features over third-party integrations.
34614
34710
 
34615
34711
  When broken or unexplained platform behavior blocks progress, send a detailed
@@ -34629,16 +34725,22 @@ column autocomplete; joins/aggregates/projections take a hand-written type or
34629
34725
  Pick<...>), env.FNxxx.call()
34630
34726
  (pass \`path\` to hit a sub-path of a function that routes internally),
34631
34727
  env.ATxxx.start(inputs?) (fire-and-forget), env.DFxxx.read.json()/.saveVersion(),
34728
+ env.FBxxx.list()/read(entryId)/upload()/createFolder(),
34632
34729
  env.STORAGE.fetch/upload/presign (upload shortens stored names over 128
34633
- characters in the middle while preserving their ends),
34730
+ characters in the middle while preserving their ends), env.STORAGE.text
34731
+ (best-effort text representation of PDF, DOCX, Excel, HTML, and text files
34732
+ without AI; \`{ vision: true }\` uses AI to represent PDFs or images as text and
34733
+ is required for images), env.STORAGE.csv (one Excel worksheet selected by name
34734
+ or zero-based index), env.STORAGE.image (PNG representation of a raster image,
34735
+ SVG, or single-page PDF),
34634
34736
  env.AI.generateText/generateObject,
34635
34737
  env.GRAPHQL.request().
34636
34738
 
34637
34739
  Not everything is available everywhere. Functions and automation code nodes
34638
34740
  also get env.STORAGE.zipTree/zipExtract \u2014 list a zip without decompressing it,
34639
- then extract only the entries you need, each into its own storage URL.
34640
- A web page does NOT have those two: expanding an archive is bulk server work
34641
- and belongs in a function the page calls.`;
34741
+ then extract only the entries you need, each into its own storage URL. A web
34742
+ page does NOT have these server-side conversion and archive helpers: that work
34743
+ belongs in a function the page calls.`;
34642
34744
  var automations = `# Automation best practices
34643
34745
 
34644
34746
  An automation's elements document is the node graph: an array of nodes, each
@@ -35134,11 +35236,7 @@ var runQuery = async (organizationId, tableSpaceId, query, params) => {
35134
35236
  return null;
35135
35237
  }
35136
35238
  })();
35137
- if (!res.ok || body?.ok === false) {
35138
- throw new Error(
35139
- body?.error ?? (raw2.trim().length > 0 ? raw2.trim() : res.status === 404 ? "table space not found or you lack access to it" : `HTTP ${String(res.status)}`)
35140
- );
35141
- }
35239
+ if (!res.ok || body?.ok === false) throw v2Error("sql", res, raw2);
35142
35240
  return {
35143
35241
  rows: body?.rows ?? [],
35144
35242
  rowCount: body?.rowCount ?? 0,
@@ -35593,20 +35691,9 @@ var runDiff = async (args) => {
35593
35691
  };
35594
35692
 
35595
35693
  // src/commands/workspace.ts
35596
- var workspaceIdFrom = (args) => {
35597
- const workspaceId = args.find(
35598
- (arg, index) => !arg.startsWith("-") && args[index - 1] !== "-o" && args[index - 1] !== "--output"
35599
- );
35600
- if (workspaceId == null) return openClone().manifest.workspaceId;
35601
- try {
35602
- openClone();
35603
- } catch (error) {
35604
- if (!(error instanceof Error) || !error.message.startsWith("Not inside a cloned workspace")) {
35605
- throw error;
35606
- }
35607
- }
35608
- return workspaceId;
35609
- };
35694
+ var workspaceIdFrom = (args) => args.find(
35695
+ (arg, index) => !arg.startsWith("-") && args[index - 1] !== "-o" && args[index - 1] !== "--output"
35696
+ ) ?? openClone().manifest.workspaceId;
35610
35697
  var listWorkspaces = async (args) => {
35611
35698
  const archived = args.includes("--archived");
35612
35699
  const limit2 = Number(flagValue(args, "--limit") ?? 200);
@@ -35646,10 +35733,7 @@ var showWorkspace = async (args) => {
35646
35733
  `Workspace ${workspaceId} not found (or not visible to you).`
35647
35734
  );
35648
35735
  }
35649
- printResult(
35650
- data.workspace,
35651
- resolveFormat(flagValue(args, "-o", "--output"))
35652
- );
35736
+ printResult(data.workspace, resolveFormat(flagValue(args, "-o", "--output")));
35653
35737
  };
35654
35738
  var listWorkspaceUsers = async (args) => {
35655
35739
  const workspaceId = workspaceIdFrom(args);
@@ -35685,9 +35769,7 @@ var listWorkspaceUsers = async (args) => {
35685
35769
  parentWorkspaceRole: membership.parentWorkspaceRole ?? ""
35686
35770
  }
35687
35771
  ]
35688
- ).sort(
35689
- (a, b) => (a.name || a.email).localeCompare(b.name || b.email)
35690
- ),
35772
+ ).sort((a, b) => (a.name || a.email).localeCompare(b.name || b.email)),
35691
35773
  resolveFormat(flagValue(args, "-o", "--output"))
35692
35774
  );
35693
35775
  };
@@ -35803,16 +35885,43 @@ it from stdin. Prefer that for anything long or quote-heavy: an inline value is
35803
35885
  parsed by your shell first, and one apostrophe inside it silently truncates the
35804
35886
  rest. Write @@ to send a literal value that starts with "@".
35805
35887
 
35888
+ Organizations: commands run as the clone you're standing in (.overai/manifest.json),
35889
+ else the active org from \`org switch\`. The active org is machine-global \u2014 every
35890
+ shell shares it \u2014 so scope one command or one shell with OVERAI_ORG_ID=<orgId>
35891
+ instead. \`overai whoami\` says which org is in effect and why.
35892
+
35806
35893
  Output: table on a TTY, JSON when piped; -o json|table|csv to force.
35807
35894
  Aliases: login = signin, logout = signout, ws = workspace.
35808
35895
  Env: OVERAI_API_URL, OVERAI_APP_URL override the target deployment.
35896
+ OVERAI_ORG_ID overrides the organization for this shell.
35809
35897
  `;
35810
35898
  process.stdout.on("error", (error) => {
35811
35899
  if (error.code === "EPIPE") process.exit(0);
35812
35900
  throw error;
35813
35901
  });
35902
+ var ORG_AGNOSTIC_COMMANDS = /* @__PURE__ */ new Set([
35903
+ "signin",
35904
+ "login",
35905
+ "signout",
35906
+ "logout",
35907
+ "whoami",
35908
+ "org",
35909
+ "clone",
35910
+ "update",
35911
+ "upgrade",
35912
+ "version",
35913
+ "--version",
35914
+ "-v",
35915
+ "help",
35916
+ "--help",
35917
+ "-h"
35918
+ ]);
35814
35919
  var run = async () => {
35815
35920
  const [command, ...rest] = process.argv.slice(2);
35921
+ if (command != null && !ORG_AGNOSTIC_COMMANDS.has(command)) {
35922
+ pinOrgFromClone();
35923
+ announceOrgScope();
35924
+ }
35816
35925
  switch (command) {
35817
35926
  case "signin":
35818
35927
  case "login":
@@ -35823,7 +35932,7 @@ var run = async () => {
35823
35932
  await signOut();
35824
35933
  return;
35825
35934
  case "whoami":
35826
- whoami();
35935
+ await whoami();
35827
35936
  return;
35828
35937
  case "org":
35829
35938
  if (rest[0] === "list") {
@@ -3,7 +3,7 @@ import {
3
3
  webPageConfigSchema,
4
4
  webPageConfigWriteSchema,
5
5
  webPageLanguage
6
- } from "./chunk-XGGDXRQI.js";
6
+ } from "./chunk-2F4ZHHFG.js";
7
7
  import "./chunk-6YTIPJTD.js";
8
8
  import "./chunk-U67V476Y.js";
9
9
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overaihq/cli",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
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",