@overaihq/cli 0.13.7 → 0.14.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/dist/index.js CHANGED
@@ -5,10 +5,10 @@ import {
5
5
  import {
6
6
  itemFilterExpressionRootSchema,
7
7
  webPageConfigSchema
8
- } from "./chunk-M2NX2CHG.js";
8
+ } from "./chunk-FTBTDB2B.js";
9
9
  import {
10
10
  external_exports
11
- } from "./chunk-XCJJTK3H.js";
11
+ } from "./chunk-PF6GZFT4.js";
12
12
  import {
13
13
  __commonJS,
14
14
  __toESM
@@ -11911,9 +11911,9 @@ var require_lib = __commonJS({
11911
11911
  }
11912
11912
  });
11913
11913
 
11914
- // ../../node_modules/.pnpm/@babel+parser@7.29.8/node_modules/@babel/parser/lib/index.js
11914
+ // ../../node_modules/.pnpm/@babel+parser@7.29.9/node_modules/@babel/parser/lib/index.js
11915
11915
  var require_lib2 = __commonJS({
11916
- "../../node_modules/.pnpm/@babel+parser@7.29.8/node_modules/@babel/parser/lib/index.js"(exports) {
11916
+ "../../node_modules/.pnpm/@babel+parser@7.29.9/node_modules/@babel/parser/lib/index.js"(exports) {
11917
11917
  "use strict";
11918
11918
  Object.defineProperty(exports, "__esModule", {
11919
11919
  value: true
@@ -14965,13 +14965,15 @@ var require_lib2 = __commonJS({
14965
14965
  const arrows = [];
14966
14966
  while (stack.length !== 0) {
14967
14967
  const node2 = stack.pop();
14968
- if (node2.type === "ArrowFunctionExpression" && node2.body.type !== "BlockStatement") {
14968
+ if (node2.type === "ArrowFunctionExpression") {
14969
14969
  if (node2.typeParameters || !node2.returnType) {
14970
14970
  this.finishArrowValidation(node2);
14971
14971
  } else {
14972
14972
  arrows.push(node2);
14973
14973
  }
14974
- stack.push(node2.body);
14974
+ if (node2.body.type !== "BlockStatement") {
14975
+ stack.push(node2.body);
14976
+ }
14975
14977
  } else if (node2.type === "ConditionalExpression") {
14976
14978
  stack.push(node2.consequent);
14977
14979
  stack.push(node2.alternate);
@@ -22127,8 +22129,11 @@ var require_lib2 = __commonJS({
22127
22129
  }
22128
22130
  }
22129
22131
  fillOptionalPropertiesForTSESLint(node) {
22130
- var _node$directive, _node$decorators, _node$optional, _node$typeAnnotation, _node$accessibility, _node$decorators2, _node$override, _node$readonly, _node$static, _node$declare, _node$returnType, _node$typeParameters, _node$optional2, _node$optional3, _node$accessibility2, _node$readonly2, _node$static2, _node$declare2, _node$definite, _node$readonly3, _node$typeAnnotation2, _node$accessibility3, _node$decorators3, _node$override2, _node$optional4, _node$id, _node$abstract, _node$declare3, _node$decorators4, _node$implements, _node$superTypeArgume, _node$typeParameters2, _node$declare4, _node$definite2, _node$const, _node$declare5, _node$computed, _node$qualifier, _node$options, _node$declare6, _node$extends, _node$optional5, _node$readonly4, _node$declare7, _node$global, _node$const2, _node$in, _node$out;
22132
+ var _node$phase, _node$directive, _node$decorators, _node$optional, _node$typeAnnotation, _node$accessibility, _node$decorators2, _node$override, _node$readonly, _node$static, _node$declare, _node$returnType, _node$typeParameters, _node$optional2, _node$optional3, _node$accessibility2, _node$readonly2, _node$static2, _node$declare2, _node$definite, _node$readonly3, _node$typeAnnotation2, _node$accessibility3, _node$decorators3, _node$override2, _node$optional4, _node$id, _node$abstract, _node$declare3, _node$decorators4, _node$implements, _node$superTypeArgume, _node$typeParameters2, _node$declare4, _node$definite2, _node$const, _node$declare5, _node$computed, _node$qualifier, _node$options, _node$declare6, _node$extends, _node$optional5, _node$readonly4, _node$declare7, _node$global, _node$const2, _node$in, _node$out;
22131
22133
  switch (node.type) {
22134
+ case "ImportDeclaration":
22135
+ (_node$phase = node.phase) != null ? _node$phase : node.phase = null;
22136
+ return;
22132
22137
  case "ExpressionStatement":
22133
22138
  (_node$directive = node.directive) != null ? _node$directive : node.directive = void 0;
22134
22139
  return;
@@ -26521,12 +26526,31 @@ var sessionSchema = external_exports.object({
26521
26526
  identityId: external_exports.string().optional(),
26522
26527
  email: external_exports.string().optional(),
26523
26528
  accessToken: external_exports.string().optional(),
26524
- accessTokenExpiresAt: external_exports.number().optional()
26529
+ accessTokenExpiresAt: external_exports.number().optional(),
26530
+ // When the refresh token itself dies. Sessions saved before this was recorded
26531
+ // have none, which reads as "unknown" rather than "expired".
26532
+ refreshTokenExpiresAt: external_exports.number().optional(),
26533
+ // Cached from `org list` so tables and the switch picker can name an org
26534
+ // without a network round trip.
26535
+ organizationName: external_exports.string().optional()
26525
26536
  });
26526
26537
  var storeSchema = external_exports.object({
26527
26538
  version: external_exports.literal(2),
26528
26539
  generation: external_exports.number().default(0),
26529
26540
  defaultApiBaseUrl: external_exports.string().optional(),
26541
+ /*
26542
+ * Remote name -> API base URL. Names exist so a tenant's cluster can be typed
26543
+ * instead of pasted; `prod` and `local` are built in (see remotes.ts) and a
26544
+ * name in use is never silently repointed. The app URL stays on the deployment
26545
+ * below, keyed by the API URL that identifies the backend.
26546
+ */
26547
+ remotes: external_exports.record(external_exports.string(), external_exports.string()).default({}),
26548
+ /*
26549
+ * Alias -> org ref (`[<remote>:]<orgId>`), expanded textually wherever an org
26550
+ * can be named. Purely local, like a shell alias: ids stay canonical in the
26551
+ * store, in clone manifests, and in everything the CLI prints.
26552
+ */
26553
+ aliases: external_exports.record(external_exports.string(), external_exports.string()).default({}),
26530
26554
  deployments: external_exports.record(
26531
26555
  external_exports.string(),
26532
26556
  external_exports.object({
@@ -26558,7 +26582,14 @@ var readStore = () => {
26558
26582
  throw error;
26559
26583
  }
26560
26584
  })();
26561
- if (raw2 == null) return { version: 2, generation: 0, deployments: {} };
26585
+ if (raw2 == null)
26586
+ return {
26587
+ version: 2,
26588
+ generation: 0,
26589
+ remotes: {},
26590
+ aliases: {},
26591
+ deployments: {}
26592
+ };
26562
26593
  const invalidStoreMessage = `Invalid credential store at ${configFilePath}. Restore a valid copy before continuing.`;
26563
26594
  const [value, parseError] = resultOfSync(() => JSON.parse(raw2));
26564
26595
  if (parseError != null) throw new Error(invalidStoreMessage);
@@ -26573,6 +26604,8 @@ var readStore = () => {
26573
26604
  version: 2,
26574
26605
  generation: 0,
26575
26606
  defaultApiBaseUrl: session.apiBaseUrl,
26607
+ remotes: {},
26608
+ aliases: {},
26576
26609
  deployments: {
26577
26610
  [session.apiBaseUrl]: {
26578
26611
  defaultOrgId: session.organizationId,
@@ -26623,7 +26656,17 @@ var appBaseUrlFromApiHost = (apiHost) => {
26623
26656
  var trimSlash = (url) => url.replace(/\/+$/, "");
26624
26657
  var DEFAULT_API_BASE_URL = "https://api.overai.com";
26625
26658
  var apiBaseUrlOverride = process.env.OVERAI_API_URL != null ? trimSlash(process.env.OVERAI_API_URL) : void 0;
26626
- var orgIdOverride = process.env.OVERAI_ORG_ID != null && process.env.OVERAI_ORG_ID !== "" ? process.env.OVERAI_ORG_ID : void 0;
26659
+ var leadingFlag = (name2) => {
26660
+ const argv = process.argv.slice(2);
26661
+ for (let i = 0; i < argv.length; i += 2) {
26662
+ if (argv[i]?.startsWith("--") !== true) return void 0;
26663
+ if (argv[i] === name2) return argv[i + 1];
26664
+ }
26665
+ return void 0;
26666
+ };
26667
+ var LEADING_FLAGS = ["--org"];
26668
+ var orgRefOverride = leadingFlag("--org") ?? (process.env.OVERAI_ORG_ID != null && process.env.OVERAI_ORG_ID !== "" ? process.env.OVERAI_ORG_ID : void 0);
26669
+ var orgRefSource = leadingFlag("--org") != null ? "--org" : orgRefOverride != null ? "OVERAI_ORG_ID" : void 0;
26627
26670
  var appBaseUrl = (backend) => {
26628
26671
  const saved = readStore();
26629
26672
  return trimSlash(
@@ -26631,6 +26674,126 @@ var appBaseUrl = (backend) => {
26631
26674
  );
26632
26675
  };
26633
26676
 
26677
+ // src/remotes.ts
26678
+ var BUILT_IN_REMOTES = {
26679
+ prod: DEFAULT_API_BASE_URL,
26680
+ local: "http://localhost:3001"
26681
+ };
26682
+ var remoteUrl = (name2, store = readStore()) => {
26683
+ const url = store.remotes[name2] ?? BUILT_IN_REMOTES[name2];
26684
+ return url == null ? void 0 : trimSlash(url);
26685
+ };
26686
+ var knownRemotes = (store = readStore()) => {
26687
+ const byUrl = new Map(
26688
+ Object.entries({ ...BUILT_IN_REMOTES, ...store.remotes }).map(
26689
+ ([name2, url]) => [trimSlash(url), name2]
26690
+ )
26691
+ );
26692
+ for (const url of Object.keys(store.deployments)) {
26693
+ const trimmed = trimSlash(url);
26694
+ if (!byUrl.has(trimmed)) byUrl.set(trimmed, remoteName(trimmed, store));
26695
+ }
26696
+ return [...byUrl].map(([apiBaseUrl2, name2]) => ({ name: name2, apiBaseUrl: apiBaseUrl2 })).sort((a, b) => a.name.localeCompare(b.name));
26697
+ };
26698
+ var derivedRemoteName = (apiBaseUrl2) => {
26699
+ const [url] = resultOfSync(() => new URL(apiBaseUrl2));
26700
+ if (url == null) return void 0;
26701
+ if ((url.hostname === "localhost" || url.hostname === "127.0.0.1") && url.port === "3001")
26702
+ return "local";
26703
+ const match = /^api\.(?:([a-z0-9-]+)\.)?(?:overai|perygee)\.com$/.exec(
26704
+ url.hostname
26705
+ );
26706
+ if (match == null) return void 0;
26707
+ return match[1] ?? "prod";
26708
+ };
26709
+ var remoteName = (apiBaseUrl2, store = readStore()) => {
26710
+ const url = trimSlash(apiBaseUrl2);
26711
+ const registered = Object.entries(store.remotes).find(
26712
+ ([, value]) => trimSlash(value) === url
26713
+ );
26714
+ if (registered != null) return registered[0];
26715
+ const builtIn = Object.entries(BUILT_IN_REMOTES).find(
26716
+ ([, value]) => value === url
26717
+ );
26718
+ if (builtIn != null) return builtIn[0];
26719
+ const derived = derivedRemoteName(url);
26720
+ return derived != null && remoteUrl(derived, store) === url ? derived : url;
26721
+ };
26722
+ var defaultApiBaseUrl = (store = readStore()) => store.defaultApiBaseUrl ?? DEFAULT_API_BASE_URL;
26723
+ var remoteForOrg = (orgId, store = readStore()) => {
26724
+ const holders = Object.entries(store.deployments).filter(([, deployment]) => deployment.sessions[orgId] != null).map(([url]) => url);
26725
+ if (holders.length <= 1) return holders[0];
26726
+ const preferred = holders.find((url) => url === store.defaultApiBaseUrl);
26727
+ if (preferred != null) return preferred;
26728
+ throw new Error(
26729
+ `Org ${orgId} is signed in on ${holders.map((url) => remoteName(url, store)).join(" and ")}. Name one, e.g. --org ${remoteName(holders[0] ?? "", store)}:${orgId}.`
26730
+ );
26731
+ };
26732
+ var remoteOnlyRef = (text, store = readStore()) => {
26733
+ const trimmed = text.trim().replace(/:$/, "");
26734
+ if (store.aliases[trimmed] != null) return void 0;
26735
+ return remoteUrl(trimmed, store);
26736
+ };
26737
+ var parseOrgRef = (text, store = readStore()) => {
26738
+ const trimmed = text.trim();
26739
+ if (trimmed === "") throw new Error("Empty organization reference.");
26740
+ const expanded = store.aliases[trimmed] ?? trimmed;
26741
+ const colon = expanded.indexOf(":");
26742
+ if (colon === -1) return { orgId: expanded };
26743
+ const name2 = expanded.slice(0, colon);
26744
+ const orgPart = expanded.slice(colon + 1).trim();
26745
+ const apiBaseUrl2 = remoteUrl(name2, store);
26746
+ if (apiBaseUrl2 == null)
26747
+ throw new Error(
26748
+ `Unknown remote "${name2}". Known: ${knownRemotes(store).map((remote) => remote.name).join(", ")}. Add one with: overai remote add ${name2} <apiUrl>`
26749
+ );
26750
+ if (orgPart === "")
26751
+ throw new Error(`Missing org id in "${expanded}". Use ${name2}:<orgId>.`);
26752
+ const aliased = store.aliases[orgPart];
26753
+ const orgId = aliased == null ? orgPart : aliased.split(":").at(-1) ?? orgPart;
26754
+ return { orgId, apiBaseUrl: apiBaseUrl2 };
26755
+ };
26756
+ var resolveOrgRef = (text, store = readStore()) => {
26757
+ const ref = parseOrgRef(text, store);
26758
+ return {
26759
+ orgId: ref.orgId,
26760
+ apiBaseUrl: ref.apiBaseUrl ?? remoteForOrg(ref.orgId, store) ?? defaultApiBaseUrl(store)
26761
+ };
26762
+ };
26763
+ var cachedOverride;
26764
+ var orgOverride = () => {
26765
+ if (cachedOverride !== void 0) return cachedOverride;
26766
+ cachedOverride = orgRefOverride == null ? null : { ...parseOrgRef(orgRefOverride), source: orgRefSource ?? "--org" };
26767
+ return cachedOverride;
26768
+ };
26769
+ var orgRefText = (orgId, apiBaseUrl2, store = readStore()) => {
26770
+ const alias = Object.entries(store.aliases).find(([name3]) => {
26771
+ const [ref] = resultOfSync(() => resolveOrgRef(name3, store));
26772
+ return ref?.orgId === orgId && ref.apiBaseUrl === trimSlash(apiBaseUrl2);
26773
+ })?.[0];
26774
+ if (alias != null) return alias;
26775
+ if (trimSlash(apiBaseUrl2) === trimSlash(defaultApiBaseUrl(store)))
26776
+ return orgId;
26777
+ const name2 = remoteName(apiBaseUrl2, store);
26778
+ return name2 === trimSlash(apiBaseUrl2) ? orgId : `${name2}:${orgId}`;
26779
+ };
26780
+ var signinHint = (orgId, apiBaseUrl2) => {
26781
+ const store = readStore();
26782
+ const url = trimSlash(apiBaseUrl2);
26783
+ return remoteName(url, store) === url && url !== trimSlash(defaultApiBaseUrl(store)) ? `Run OVERAI_API_URL=${url} overai signin ${orgId}.` : `Run overai signin ${orgRefText(orgId, apiBaseUrl2, store)}.`;
26784
+ };
26785
+ var expiresIn = (at) => {
26786
+ if (at == null) return void 0;
26787
+ const ms = at - Date.now();
26788
+ if (ms <= 0) return "expired";
26789
+ const hours = Math.floor(ms / 36e5);
26790
+ if (hours < 1) return `${String(Math.max(1, Math.floor(ms / 6e4)))}m`;
26791
+ return hours < 48 ? `${String(hours)}h` : `${String(Math.floor(hours / 24))}d`;
26792
+ };
26793
+
26794
+ // ../common/src/stdlib/iife.ts
26795
+ var iife = (fn2) => fn2();
26796
+
26634
26797
  // ../common/src/stdlib/omit.ts
26635
26798
  var omit = (obj, ...keys) => {
26636
26799
  const result = { ...obj };
@@ -26640,7 +26803,29 @@ var omit = (obj, ...keys) => {
26640
26803
 
26641
26804
  // src/api.ts
26642
26805
  var commandApiBaseUrl;
26643
- var apiBaseUrl = () => commandApiBaseUrl ??= apiBaseUrlOverride ?? readStore().defaultApiBaseUrl ?? DEFAULT_API_BASE_URL;
26806
+ var explicitRemote;
26807
+ var cloneRemote;
26808
+ var setRemoteScope = (apiBaseUrl_) => {
26809
+ if (commandApiBaseUrl != null && commandApiBaseUrl !== apiBaseUrl_)
26810
+ throw new Error("Cannot change remote after a command has started.");
26811
+ explicitRemote = apiBaseUrl_;
26812
+ };
26813
+ var setCloneRemote = (apiBaseUrl_, organizationId) => {
26814
+ cloneRemote = { apiBaseUrl: apiBaseUrl_, organizationId };
26815
+ };
26816
+ var apiBaseUrl = () => commandApiBaseUrl ??= iife(() => {
26817
+ if (apiBaseUrlOverride != null) return apiBaseUrlOverride;
26818
+ if (explicitRemote != null) return explicitRemote;
26819
+ const store = readStore();
26820
+ const override = orgOverride();
26821
+ if (override?.apiBaseUrl != null) return override.apiBaseUrl;
26822
+ if (cloneRemote != null && (override == null || override.orgId === cloneRemote.organizationId))
26823
+ return cloneRemote.apiBaseUrl;
26824
+ const overrideHome = override == null ? void 0 : remoteForOrg(override.orgId, store);
26825
+ if (overrideHome != null) return overrideHome;
26826
+ if (cloneRemote != null) return cloneRemote.apiBaseUrl;
26827
+ return (orgScopeOverride == null ? void 0 : remoteForOrg(orgScopeOverride, store)) ?? defaultApiBaseUrl(store);
26828
+ });
26644
26829
  var AuthRpcError = class extends Error {
26645
26830
  constructor(status, message) {
26646
26831
  super(message);
@@ -26674,10 +26859,11 @@ var trpcMutation = async (procedure, input, baseUrl = apiBaseUrl()) => {
26674
26859
  return trpcResult(res, procedure);
26675
26860
  };
26676
26861
  var exchangeCliAuthCode = (code, codeVerifier) => trpcMutation("exchangeCliAuthCode", { code, codeVerifier });
26677
- var exchangeSessionToken = (refreshToken, destinationOrgId) => trpcMutation("exchangeSessionToken", {
26678
- refreshToken,
26679
- destinationOrgId
26680
- });
26862
+ var exchangeSessionToken = (refreshToken, destinationOrgId, backend = apiBaseUrl()) => trpcMutation(
26863
+ "exchangeSessionToken",
26864
+ { refreshToken, destinationOrgId },
26865
+ backend
26866
+ );
26681
26867
  var signOutHeadless = (refreshToken, baseUrl = apiBaseUrl()) => trpcMutation(
26682
26868
  "signOutHeadless",
26683
26869
  { refreshToken },
@@ -26711,7 +26897,7 @@ var context = () => {
26711
26897
  if (commandContext != null) return commandContext;
26712
26898
  const store = readStore();
26713
26899
  const deployment = store.deployments[apiBaseUrl()];
26714
- const organizationId = orgIdOverride ?? orgScopeOverride ?? deployment?.defaultOrgId;
26900
+ const organizationId = orgOverride()?.orgId ?? orgScopeOverride ?? deployment?.defaultOrgId;
26715
26901
  if (organizationId == null)
26716
26902
  throw new InteractiveSignInRequired("Not signed in. Run `overai signin`.");
26717
26903
  const session = deployment?.sessions[organizationId] ?? Object.values(deployment?.sessions ?? {})[0];
@@ -26736,7 +26922,7 @@ var announceOrgScope = () => {
26736
26922
  const org = scopedOrgId();
26737
26923
  if (org == null) return;
26738
26924
  process.stderr.write(
26739
- `Using org ${org} (${orgIdOverride != null ? "OVERAI_ORG_ID" : orgScopeOverride != null ? ".overai/manifest.json" : "saved default"}).
26925
+ `Using org ${org} on ${remoteName(apiBaseUrl())} (${orgOverride()?.source ?? (orgScopeOverride != null ? ".overai/manifest.json" : "saved default")}).
26740
26926
  `
26741
26927
  );
26742
26928
  };
@@ -26749,7 +26935,7 @@ var getAccessToken = async () => {
26749
26935
  "All CLI sessions were signed out in another process. Run the command again."
26750
26936
  );
26751
26937
  const deployment = store.deployments[baseUrl];
26752
- const signin = `Run OVERAI_ORG_ID=${target.organizationId} overai signin.`;
26938
+ const signin = signinHint(target.organizationId, baseUrl);
26753
26939
  if (deployment == null) throw new InteractiveSignInRequired(signin);
26754
26940
  if ((deployment.signedOut[target.organizationId] ?? 0) !== target.signedOut)
26755
26941
  throw new Error(`This org was signed out in another process. ${signin}`);
@@ -26815,6 +27001,7 @@ var getAccessToken = async () => {
26815
27001
  source.refreshToken = refreshed.refreshToken;
26816
27002
  source.accessToken = refreshed.token;
26817
27003
  source.accessTokenExpiresAt = Date.now() + refreshed.accessTokenValiditySeconds * 1e3;
27004
+ source.refreshTokenExpiresAt = refreshed.refreshTokenValiditySeconds == null ? void 0 : Date.now() + refreshed.refreshTokenValiditySeconds * 1e3;
26818
27005
  writeStore(store);
26819
27006
  }
26820
27007
  const sourceSessionId = decodeJwtClaims(source.accessToken).sid;
@@ -26875,9 +27062,11 @@ var getAccessToken = async () => {
26875
27062
  apiBaseUrl: baseUrl,
26876
27063
  identityId: claims.user_id,
26877
27064
  email: claims.email ?? target.email,
27065
+ organizationName: existing?.organizationName,
26878
27066
  refreshToken: result.refreshToken,
26879
27067
  accessToken: result.token,
26880
- accessTokenExpiresAt: Date.now() + result.accessTokenValiditySeconds * 1e3
27068
+ accessTokenExpiresAt: Date.now() + result.accessTokenValiditySeconds * 1e3,
27069
+ refreshTokenExpiresAt: result.refreshTokenValiditySeconds == null ? void 0 : Date.now() + result.refreshTokenValiditySeconds * 1e3
26881
27070
  };
26882
27071
  deployment.pending = omit(deployment.pending, target.organizationId);
26883
27072
  writeStore(store);
@@ -26885,6 +27074,34 @@ var getAccessToken = async () => {
26885
27074
  return { token: result.token, organizationId: target.organizationId };
26886
27075
  });
26887
27076
  };
27077
+ var accessTokenForSavedSession = async (backend, organizationId) => withStoreLock(async (store) => {
27078
+ const session = store.deployments[backend]?.sessions[organizationId];
27079
+ if (session == null)
27080
+ throw new InteractiveSignInRequired(
27081
+ signinHint(organizationId, backend)
27082
+ );
27083
+ if (session.accessToken != null && (session.accessTokenExpiresAt ?? 0) > Date.now() + 6e4)
27084
+ return session.accessToken;
27085
+ const result = await exchangeSessionToken(
27086
+ session.refreshToken,
27087
+ null,
27088
+ backend
27089
+ );
27090
+ if (result.type !== "success")
27091
+ throw new InteractiveSignInRequired(
27092
+ signinHint(organizationId, backend)
27093
+ );
27094
+ if (decodeJwtClaims(result.token).org !== organizationId)
27095
+ throw new Error(
27096
+ "The auth server returned a session for a different org or identity."
27097
+ );
27098
+ session.refreshToken = result.refreshToken;
27099
+ session.accessToken = result.token;
27100
+ session.accessTokenExpiresAt = Date.now() + result.accessTokenValiditySeconds * 1e3;
27101
+ session.refreshTokenExpiresAt = result.refreshTokenValiditySeconds == null ? void 0 : Date.now() + result.refreshTokenValiditySeconds * 1e3;
27102
+ writeStore(store);
27103
+ return result.token;
27104
+ });
26888
27105
  var v2Fetch = async (path, init) => {
26889
27106
  const auth = await getAccessToken();
26890
27107
  const headers = new Headers(init?.headers);
@@ -26944,13 +27161,50 @@ import { randomBytes as randomBytes2 } from "crypto";
26944
27161
  import { createServer } from "http";
26945
27162
  import { hostname } from "os";
26946
27163
 
27164
+ // src/args.ts
27165
+ import { readFileSync as readFileSync2 } from "fs";
27166
+ var flagValue = (args, ...names) => {
27167
+ for (const name2 of names) {
27168
+ const i = args.indexOf(name2);
27169
+ if (i !== -1 && i + 1 < args.length) return args[i + 1];
27170
+ }
27171
+ return void 0;
27172
+ };
27173
+ var stdinClaimedBy = null;
27174
+ var readStdin = async (claimant = "stdin") => {
27175
+ if (stdinClaimedBy != null) {
27176
+ throw new Error(
27177
+ `Only one value can be read from stdin; ${stdinClaimedBy} already consumed it.`
27178
+ );
27179
+ }
27180
+ stdinClaimedBy = claimant;
27181
+ const chunks = [];
27182
+ for await (const chunk of process.stdin) {
27183
+ chunks.push(chunk);
27184
+ }
27185
+ return Buffer.concat(chunks).toString("utf8");
27186
+ };
27187
+ var readValue = async (value, label) => {
27188
+ if (value?.startsWith("@") !== true) return value;
27189
+ if (value.startsWith("@@")) return value.slice(1);
27190
+ const path = value.slice(1);
27191
+ if (path === "-") return (await readStdin(`${label} @-`)).trim();
27192
+ try {
27193
+ return readFileSync2(path, "utf8").trim();
27194
+ } catch {
27195
+ throw new Error(
27196
+ `${label}: could not read file "${path}". Write "@@" to send a literal value starting with "@".`
27197
+ );
27198
+ }
27199
+ };
27200
+
26947
27201
  // src/browser.ts
26948
27202
  import { spawn } from "child_process";
26949
27203
  import { platform } from "os";
26950
27204
  var openBrowser = (url) => {
26951
- const [command, args] = platform() === "darwin" ? ["open", [url]] : platform() === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
27205
+ const [command2, args] = platform() === "darwin" ? ["open", [url]] : platform() === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
26952
27206
  try {
26953
- const child = spawn(command, args, { stdio: "ignore", detached: true });
27207
+ const child = spawn(command2, args, { stdio: "ignore", detached: true });
26954
27208
  child.on("error", () => {
26955
27209
  });
26956
27210
  child.unref();
@@ -26958,6 +27212,60 @@ var openBrowser = (url) => {
26958
27212
  }
26959
27213
  };
26960
27214
 
27215
+ // src/output.ts
27216
+ var resolveFormat = (explicit) => {
27217
+ if (explicit === "json" || explicit === "csv" || explicit === "table") {
27218
+ return explicit;
27219
+ }
27220
+ if (explicit != null) {
27221
+ throw new Error(
27222
+ `Unknown output format "${explicit}". Use json, table, or csv.`
27223
+ );
27224
+ }
27225
+ return process.stdout.isTTY ? "table" : "json";
27226
+ };
27227
+ var cell = (value) => {
27228
+ if (value == null) return "";
27229
+ if (typeof value === "string") return value;
27230
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
27231
+ return String(value);
27232
+ }
27233
+ return JSON.stringify(value);
27234
+ };
27235
+ var formatResult = (data, format2) => {
27236
+ if (format2 === "json" || !Array.isArray(data)) {
27237
+ return JSON.stringify(data, null, 2);
27238
+ }
27239
+ const records = data.map(
27240
+ (row) => row != null && typeof row === "object" ? row : { value: row }
27241
+ );
27242
+ if (records.length === 0) return "(no rows)";
27243
+ const columns = [...new Set(records.flatMap((r) => Object.keys(r)))];
27244
+ if (format2 === "csv") {
27245
+ const escape2 = (value) => {
27246
+ const text = cell(value);
27247
+ return /[",\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
27248
+ };
27249
+ return [
27250
+ columns.join(","),
27251
+ ...records.map((r) => columns.map((c) => escape2(r[c])).join(","))
27252
+ ].join("\n");
27253
+ }
27254
+ const widths = columns.map(
27255
+ (c) => Math.max(c.length, ...records.map((r) => cell(r[c]).length))
27256
+ );
27257
+ const line = (cells) => cells.map((text, i) => text.padEnd(widths[i] ?? 0)).join(" ");
27258
+ return [
27259
+ line(columns),
27260
+ line(widths.map((w) => "-".repeat(w))),
27261
+ ...records.map((r) => line(columns.map((c) => cell(r[c]))))
27262
+ ].join("\n");
27263
+ };
27264
+ var printResult = (data, format2) => {
27265
+ process.stdout.write(`${formatResult(data, format2)}
27266
+ `);
27267
+ };
27268
+
26961
27269
  // src/pkce.ts
26962
27270
  import { createHash, randomBytes } from "crypto";
26963
27271
  var createPkcePair = () => {
@@ -26967,7 +27275,7 @@ var createPkcePair = () => {
26967
27275
  };
26968
27276
 
26969
27277
  // src/workspace/manifest.ts
26970
- import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
27278
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
26971
27279
  import { dirname, join as join2, resolve } from "path";
26972
27280
  var MANIFEST_DIR = ".overai";
26973
27281
  var MANIFEST_FILE = "manifest.json";
@@ -26986,22 +27294,34 @@ var findWorkspaceRoot = (from = process.cwd()) => {
26986
27294
  }
26987
27295
  };
26988
27296
  var readManifest = (root) => JSON.parse(
26989
- readFileSync2(join2(root, MANIFEST_DIR, MANIFEST_FILE), "utf8")
27297
+ readFileSync3(join2(root, MANIFEST_DIR, MANIFEST_FILE), "utf8")
26990
27298
  );
26991
27299
  var openClone = () => {
26992
27300
  const root = findWorkspaceRoot();
26993
27301
  const manifest = readManifest(root);
26994
- if (orgIdOverride != null && orgIdOverride !== manifest.organizationId) {
27302
+ const override = orgOverride();
27303
+ if (override != null && override.orgId !== manifest.organizationId) {
26995
27304
  throw new Error(
26996
- `OVERAI_ORG_ID=${orgIdOverride} conflicts with this clone's org ${manifest.organizationId}. Unset it before syncing.`
27305
+ `${override.source}=${override.orgId} conflicts with this clone's org ${manifest.organizationId}. Drop it before syncing.`
26997
27306
  );
26998
27307
  }
27308
+ const namedRemote = override?.apiBaseUrl ?? apiBaseUrlOverride;
27309
+ if (namedRemote != null && manifest.apiBaseUrl != null && namedRemote !== manifest.apiBaseUrl) {
27310
+ throw new Error(
27311
+ `${override?.apiBaseUrl == null ? "OVERAI_API_URL" : override.source} names remote ${remoteName(namedRemote)}; this clone came from ${remoteName(manifest.apiBaseUrl)}.`
27312
+ );
27313
+ }
27314
+ if (manifest.apiBaseUrl != null)
27315
+ setCloneRemote(manifest.apiBaseUrl, manifest.organizationId);
26999
27316
  setOrgScope(manifest.organizationId);
27000
27317
  return { root, manifest };
27001
27318
  };
27002
27319
  var pinOrgFromClone = () => {
27003
27320
  try {
27004
- setOrgScope(readManifest(findWorkspaceRoot()).organizationId);
27321
+ const manifest = readManifest(findWorkspaceRoot());
27322
+ if (manifest.apiBaseUrl != null)
27323
+ setCloneRemote(manifest.apiBaseUrl, manifest.organizationId);
27324
+ setOrgScope(manifest.organizationId);
27005
27325
  } catch {
27006
27326
  }
27007
27327
  };
@@ -27022,14 +27342,14 @@ var writeSynced = (root, relPath, content) => {
27022
27342
  };
27023
27343
  var readWorkingFile = (root, relPath) => {
27024
27344
  try {
27025
- return readFileSync2(join2(root, relPath), "utf8");
27345
+ return readFileSync3(join2(root, relPath), "utf8");
27026
27346
  } catch {
27027
27347
  return null;
27028
27348
  }
27029
27349
  };
27030
27350
  var readBaseFile = (root, relPath) => {
27031
27351
  try {
27032
- return readFileSync2(basePath(root, relPath), "utf8");
27352
+ return readFileSync3(basePath(root, relPath), "utf8");
27033
27353
  } catch {
27034
27354
  return null;
27035
27355
  }
@@ -27116,11 +27436,28 @@ var startCallbackServer = (expectedState) => {
27116
27436
  });
27117
27437
  });
27118
27438
  };
27119
- var signIn = async (requestedOrgId = orgIdOverride) => {
27439
+ var signIn = async (target = orgRefOverride) => {
27440
+ const requestedOrgId = (() => {
27441
+ if (target == null) return void 0;
27442
+ if (target.startsWith("http://") || target.startsWith("https://")) {
27443
+ setRemoteScope(trimSlash(target));
27444
+ return orgOverride()?.orgId;
27445
+ }
27446
+ const remote = remoteOnlyRef(target);
27447
+ if (remote != null) {
27448
+ setRemoteScope(remote);
27449
+ return orgOverride()?.orgId;
27450
+ }
27451
+ const ref = parseOrgRef(target);
27452
+ if (ref.apiBaseUrl != null) setRemoteScope(ref.apiBaseUrl);
27453
+ return ref.orgId;
27454
+ })();
27120
27455
  const baseUrl = apiBaseUrl();
27121
27456
  const browserBaseUrl = appBaseUrl(baseUrl);
27122
27457
  if (browserBaseUrl === "")
27123
- throw new Error("Set OVERAI_APP_URL for this backend before signing in.");
27458
+ throw new Error(
27459
+ `No web address known for ${remoteName(baseUrl)}. Record one with: overai remote add <name> ${baseUrl} --app <appUrl>`
27460
+ );
27124
27461
  const initialStore = readStore();
27125
27462
  const { verifier, challenge } = createPkcePair();
27126
27463
  const state = randomBytes2(16).toString("base64url");
@@ -27183,12 +27520,16 @@ ${authorizeUrl}
27183
27520
  identityId: claims.user_id,
27184
27521
  email,
27185
27522
  accessToken: session.token,
27186
- accessTokenExpiresAt: Date.now() + session.accessTokenValiditySeconds * 1e3
27523
+ accessTokenExpiresAt: Date.now() + session.accessTokenValiditySeconds * 1e3,
27524
+ refreshTokenExpiresAt: Date.now() + session.refreshTokenValiditySeconds * 1e3
27187
27525
  };
27188
27526
  deployment.pending = omit(deployment.pending, session.organizationId);
27189
27527
  deployment.appBaseUrl = browserBaseUrl;
27190
27528
  deployment.defaultOrgId = session.organizationId;
27191
27529
  store.defaultApiBaseUrl = baseUrl;
27530
+ const derived = derivedRemoteName(baseUrl);
27531
+ if (derived != null && remoteUrl(derived, store) == null && !Object.values(store.remotes).some((url) => trimSlash(url) === baseUrl))
27532
+ store.remotes[derived] = baseUrl;
27192
27533
  writeStore(store);
27193
27534
  });
27194
27535
  } catch (error) {
@@ -27196,22 +27537,40 @@ ${authorizeUrl}
27196
27537
  throw error;
27197
27538
  }
27198
27539
  process.stdout.write(
27199
- `Signed in${email != null ? ` as ${email}` : ""} (org ${session.organizationId}).
27540
+ `Signed in${email != null ? ` as ${email}` : ""} to org ${session.organizationId} on ${remoteName(baseUrl)}.
27541
+ This is now the default for commands that don't name an org.
27200
27542
  `
27201
27543
  );
27202
27544
  } finally {
27203
27545
  close();
27204
27546
  }
27205
27547
  };
27206
- var signOut = async (all = false) => {
27207
- const root = (() => {
27208
- try {
27209
- return findWorkspaceRoot();
27210
- } catch {
27211
- return null;
27548
+ var signOut = async (all = false, target) => {
27549
+ if (target != null) {
27550
+ const ref = resolveOrgRef(target);
27551
+ const override = orgOverride();
27552
+ if (override != null && override.orgId !== ref.orgId) {
27553
+ throw new Error(
27554
+ `${override.source}=${override.orgId} conflicts with the requested org ${ref.orgId}. Drop it first.`
27555
+ );
27212
27556
  }
27213
- })();
27214
- if (root != null) setOrgScope(readManifest(root).organizationId);
27557
+ setRemoteScope(ref.apiBaseUrl);
27558
+ setOrgScope(ref.orgId);
27559
+ } else {
27560
+ const root = (() => {
27561
+ try {
27562
+ return findWorkspaceRoot();
27563
+ } catch {
27564
+ return null;
27565
+ }
27566
+ })();
27567
+ if (root != null) {
27568
+ const manifest = readManifest(root);
27569
+ if (manifest.apiBaseUrl != null)
27570
+ setCloneRemote(manifest.apiBaseUrl, manifest.organizationId);
27571
+ setOrgScope(manifest.organizationId);
27572
+ }
27573
+ }
27215
27574
  const baseUrl = apiBaseUrl();
27216
27575
  const orgId = scopedOrgId();
27217
27576
  await withStoreLock(async (store) => {
@@ -27265,11 +27624,11 @@ var signOut = async (all = false) => {
27265
27624
  );
27266
27625
  });
27267
27626
  process.stdout.write(
27268
- all ? "Signed out of all locally saved CLI sessions.\n" : `Signed out of org ${orgId ?? "(none selected)"}.
27627
+ all ? "Signed out of all locally saved CLI sessions.\n" : `Signed out of org ${orgId ?? "(none selected)"}${orgId == null ? "" : ` on ${remoteName(baseUrl)}`}.
27269
27628
  `
27270
27629
  );
27271
27630
  };
27272
- var whoami = () => {
27631
+ var whoami = (args = []) => {
27273
27632
  const root = (() => {
27274
27633
  try {
27275
27634
  return findWorkspaceRoot();
@@ -27277,108 +27636,40 @@ var whoami = () => {
27277
27636
  return null;
27278
27637
  }
27279
27638
  })();
27280
- if (root != null) setOrgScope(readManifest(root).organizationId);
27281
- const org = scopedOrgId();
27282
- const deployment = readStore().deployments[apiBaseUrl()];
27283
- const session = org == null ? void 0 : deployment?.sessions[org];
27284
- process.stdout.write(
27285
- `Backend: ${apiBaseUrl()}
27286
- Org: ${org ?? "none"}
27287
- Selected by: ${orgIdOverride != null ? "OVERAI_ORG_ID" : root != null ? ".overai/manifest.json" : "saved default"}
27288
- Identity: ${session?.email ?? session?.identityId ?? "no saved session for this org"}
27289
- `
27290
- );
27291
- };
27292
-
27293
- // src/args.ts
27294
- import { readFileSync as readFileSync3 } from "fs";
27295
- var flagValue = (args, ...names) => {
27296
- for (const name2 of names) {
27297
- const i = args.indexOf(name2);
27298
- if (i !== -1 && i + 1 < args.length) return args[i + 1];
27299
- }
27300
- return void 0;
27301
- };
27302
- var stdinClaimedBy = null;
27303
- var readStdin = async (claimant = "stdin") => {
27304
- if (stdinClaimedBy != null) {
27305
- throw new Error(
27306
- `Only one value can be read from stdin; ${stdinClaimedBy} already consumed it.`
27307
- );
27308
- }
27309
- stdinClaimedBy = claimant;
27310
- const chunks = [];
27311
- for await (const chunk of process.stdin) {
27312
- chunks.push(chunk);
27639
+ if (root != null) {
27640
+ const manifest = readManifest(root);
27641
+ if (manifest.apiBaseUrl != null)
27642
+ setCloneRemote(manifest.apiBaseUrl, manifest.organizationId);
27643
+ setOrgScope(manifest.organizationId);
27313
27644
  }
27314
- return Buffer.concat(chunks).toString("utf8");
27315
- };
27316
- var readValue = async (value, label) => {
27317
- if (value?.startsWith("@") !== true) return value;
27318
- if (value.startsWith("@@")) return value.slice(1);
27319
- const path = value.slice(1);
27320
- if (path === "-") return (await readStdin(`${label} @-`)).trim();
27321
- try {
27322
- return readFileSync3(path, "utf8").trim();
27323
- } catch {
27324
- throw new Error(
27325
- `${label}: could not read file "${path}". Write "@@" to send a literal value starting with "@".`
27326
- );
27327
- }
27328
- };
27329
-
27330
- // src/output.ts
27331
- var resolveFormat = (explicit) => {
27332
- if (explicit === "json" || explicit === "csv" || explicit === "table") {
27333
- return explicit;
27334
- }
27335
- if (explicit != null) {
27336
- throw new Error(
27337
- `Unknown output format "${explicit}". Use json, table, or csv.`
27338
- );
27339
- }
27340
- return process.stdout.isTTY ? "table" : "json";
27341
- };
27342
- var cell = (value) => {
27343
- if (value == null) return "";
27344
- if (typeof value === "string") return value;
27345
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
27346
- return String(value);
27347
- }
27348
- return JSON.stringify(value);
27349
- };
27350
- var formatResult = (data, format2) => {
27351
- if (format2 === "json" || !Array.isArray(data)) {
27352
- return JSON.stringify(data, null, 2);
27353
- }
27354
- const records = data.map(
27355
- (row) => row != null && typeof row === "object" ? row : { value: row }
27356
- );
27357
- if (records.length === 0) return "(no rows)";
27358
- const columns = [...new Set(records.flatMap((r) => Object.keys(r)))];
27359
- if (format2 === "csv") {
27360
- const escape2 = (value) => {
27361
- const text = cell(value);
27362
- return /[",\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
27363
- };
27364
- return [
27365
- columns.join(","),
27366
- ...records.map((r) => columns.map((c) => escape2(r[c])).join(","))
27367
- ].join("\n");
27645
+ const store = readStore();
27646
+ const org = scopedOrgId();
27647
+ const backend = apiBaseUrl();
27648
+ const session = org == null ? void 0 : store.deployments[backend]?.sessions[org];
27649
+ const facts = {
27650
+ remote: remoteName(backend, store),
27651
+ apiBaseUrl: backend,
27652
+ org: org ?? "none",
27653
+ orgName: session?.organizationName ?? "",
27654
+ ref: org == null ? "" : orgRefText(org, backend, store),
27655
+ selectedBy: orgOverride()?.source ?? (root != null ? ".overai/manifest.json" : "saved default"),
27656
+ identity: session?.email ?? session?.identityId ?? "no saved session for this org",
27657
+ sessionExpires: expiresIn(session?.refreshTokenExpiresAt) ?? "unknown"
27658
+ };
27659
+ const format2 = resolveFormat(flagValue(args, "-o", "--output"));
27660
+ if (format2 !== "table") {
27661
+ printResult(facts, format2);
27662
+ return;
27368
27663
  }
27369
- const widths = columns.map(
27370
- (c) => Math.max(c.length, ...records.map((r) => cell(r[c]).length))
27664
+ process.stdout.write(
27665
+ [
27666
+ `Remote: ${facts.remote} (${facts.apiBaseUrl})`,
27667
+ `Org: ${facts.org}${facts.orgName === "" ? "" : ` (${facts.orgName})`}`,
27668
+ `Selected: ${facts.selectedBy}`,
27669
+ `Identity: ${facts.identity}`,
27670
+ `Session: ${facts.sessionExpires === "unknown" ? "expiry unknown" : facts.sessionExpires === "expired" ? "expired \u2014 sign in again" : `expires in ${facts.sessionExpires}`}`
27671
+ ].join("\n") + "\n"
27371
27672
  );
27372
- const line = (cells) => cells.map((text, i) => text.padEnd(widths[i] ?? 0)).join(" ");
27373
- return [
27374
- line(columns),
27375
- line(widths.map((w) => "-".repeat(w))),
27376
- ...records.map((r) => line(columns.map((c) => cell(r[c]))))
27377
- ].join("\n");
27378
- };
27379
- var printResult = (data, format2) => {
27380
- process.stdout.write(`${formatResult(data, format2)}
27381
- `);
27382
27673
  };
27383
27674
 
27384
27675
  // src/commands/agent.ts
@@ -27517,11 +27808,11 @@ var runAutomation = async (args) => {
27517
27808
  // src/workspace/validateElements.ts
27518
27809
  var findings = (parsed) => parsed.success ? [] : parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
27519
27810
  var validateAutomationElements = async (elements) => {
27520
- const { automationNodesSchema } = await import("./types-M4YAYTNG.js");
27811
+ const { automationNodesSchema } = await import("./types-3BS55ING.js");
27521
27812
  return findings(automationNodesSchema.safeParse(elements));
27522
27813
  };
27523
27814
  var validateWebPageConfig = async (config) => {
27524
- const { webPageConfigWriteSchema } = await import("./webPage-3MNCKCRX.js");
27815
+ const { webPageConfigWriteSchema } = await import("./webPage-7BXDGWX5.js");
27525
27816
  return findings(webPageConfigWriteSchema.safeParse(config));
27526
27817
  };
27527
27818
 
@@ -34257,6 +34548,7 @@ var runClone = async (args, isShallow = false) => {
34257
34548
  workspaceId: entry.workspace.id,
34258
34549
  workspaceName: entry.workspace.name,
34259
34550
  organizationId: entry.workspace.organizationId,
34551
+ apiBaseUrl: apiBaseUrl(),
34260
34552
  resources: {}
34261
34553
  },
34262
34554
  resourcesByAdapter
@@ -35130,6 +35422,7 @@ var ID_PREFIXES = {
35130
35422
  },
35131
35423
  snaptrade_user: { prefix: "stu", body: 12 },
35132
35424
  storage_transcription: { prefix: "stc", body: 10 },
35425
+ storage_transcription_job: { prefix: "stj", body: 10 },
35133
35426
  storage_vision_conversion: {
35134
35427
  prefix: "svc",
35135
35428
  body: 20,
@@ -35249,6 +35542,7 @@ var runOpen = async (args) => {
35249
35542
  };
35250
35543
 
35251
35544
  // src/commands/org.ts
35545
+ import { createInterface } from "readline/promises";
35252
35546
  var USER_ORGS_QUERY = `
35253
35547
  query UserOrganizations($identityId: String!) {
35254
35548
  identityOrganizations(condition: { identityId: $identityId }) {
@@ -35259,55 +35553,369 @@ var USER_ORGS_QUERY = `
35259
35553
  }
35260
35554
  }
35261
35555
  `;
35262
- var listOrgRows = async () => {
35263
- const auth = await getAccessToken();
35264
- const identityId = decodeJwtClaims(auth.token).user_id;
35265
- if (identityId == null) {
35556
+ var collectOrgs = async () => {
35557
+ const store = readStore();
35558
+ const aliasFor = (orgId, backend) => Object.entries(store.aliases).find(([alias]) => {
35559
+ const [ref] = resultOfSync(() => resolveOrgRef(alias, store));
35560
+ return ref?.orgId === orgId && trimSlash(ref.apiBaseUrl) === backend;
35561
+ })?.[0] ?? "";
35562
+ const activeBackend = defaultApiBaseUrl(store);
35563
+ const rows = await Promise.all(
35564
+ Object.entries(store.deployments).filter(([, deployment]) => Object.keys(deployment.sessions).length > 0).map(async ([backend, deployment]) => {
35565
+ const name2 = remoteName(backend, store);
35566
+ const asOrg = deployment.defaultOrgId != null && deployment.sessions[deployment.defaultOrgId] != null ? deployment.defaultOrgId : Object.keys(deployment.sessions)[0] ?? "";
35567
+ const [memberships, failure] = await fetchOrgNames(backend, asOrg).then(
35568
+ (names) => [names, void 0],
35569
+ (error) => [
35570
+ /* @__PURE__ */ new Map(),
35571
+ error instanceof Error ? error.message : String(error)
35572
+ ]
35573
+ );
35574
+ const ids = /* @__PURE__ */ new Set([
35575
+ ...Object.keys(deployment.sessions),
35576
+ ...memberships.keys()
35577
+ ]);
35578
+ return [...ids].map((id) => {
35579
+ const session = deployment.sessions[id];
35580
+ return {
35581
+ active: backend === activeBackend && deployment.defaultOrgId === id,
35582
+ alias: aliasFor(id, backend),
35583
+ name: memberships.get(id) ?? session?.organizationName ?? "",
35584
+ id,
35585
+ remote: name2,
35586
+ apiBaseUrl: backend,
35587
+ session: failure ?? (session == null ? "signin required" : expiresIn(session.refreshTokenExpiresAt) ?? "saved")
35588
+ };
35589
+ });
35590
+ })
35591
+ );
35592
+ return rows.flat().sort(
35593
+ (a, b) => a.remote.localeCompare(b.remote) || a.name.localeCompare(b.name) || a.id.localeCompare(b.id)
35594
+ );
35595
+ };
35596
+ var fetchOrgNames = async (backend, organizationId) => {
35597
+ const token = await accessTokenForSavedSession(backend, organizationId);
35598
+ const identityId = decodeJwtClaims(token).user_id;
35599
+ if (identityId == null)
35266
35600
  throw new Error("Could not read identity from the current session.");
35267
- }
35268
- const data = await gqlRequest(USER_ORGS_QUERY, { identityId });
35269
- return data.identityOrganizations.nodes.filter((node) => node != null && !node.disabled).map((node) => node?.organization).filter((org) => org != null);
35601
+ const response = await fetch(`${backend}/v2/graphql`, {
35602
+ method: "POST",
35603
+ headers: {
35604
+ "Content-Type": "application/json",
35605
+ Authorization: `Bearer ${token}`
35606
+ },
35607
+ body: JSON.stringify({
35608
+ query: USER_ORGS_QUERY,
35609
+ variables: { identityId }
35610
+ }),
35611
+ signal: AbortSignal.timeout(3e4)
35612
+ });
35613
+ const body = await response.json().catch(() => null);
35614
+ const failure = body?.errors?.[0]?.message;
35615
+ if (failure != null) throw new Error(failure);
35616
+ if (body?.data == null)
35617
+ throw new Error(`HTTP ${String(response.status)} listing organizations`);
35618
+ return new Map(
35619
+ body.data.identityOrganizations.nodes.filter((node) => node != null && !node.disabled).map((node) => node?.organization).filter((org) => org != null).map((org) => [org.id, org.name ?? ""])
35620
+ );
35270
35621
  };
35271
- var listOrgs = async (args) => {
35272
- const active = scopedOrgId();
35622
+ var cacheOrgNames = async (rows) => {
35623
+ const named = rows.filter((row) => row.name !== "");
35624
+ if (named.length === 0) return;
35625
+ await withStoreLock((store) => {
35626
+ let changed = false;
35627
+ for (const row of named) {
35628
+ const session = store.deployments[row.apiBaseUrl]?.sessions[row.id];
35629
+ if (session == null || session.organizationName === row.name) continue;
35630
+ session.organizationName = row.name;
35631
+ changed = true;
35632
+ }
35633
+ if (changed) writeStore(store);
35634
+ });
35635
+ };
35636
+ var printOrgs = (rows, format2) => {
35637
+ const resolved = resolveFormat(format2);
35638
+ if (resolved !== "table") {
35639
+ printResult(rows, resolved);
35640
+ return;
35641
+ }
35642
+ const multiRemote = new Set(rows.map((row) => row.remote)).size > 1;
35643
+ const aliased = rows.some((row) => row.alias !== "");
35273
35644
  printResult(
35274
- (await listOrgRows()).map((org) => ({
35275
- id: org.id,
35276
- name: org.name ?? "",
35277
- active: org.id === active,
35278
- sessionSaved: readStore().deployments[apiBaseUrl()]?.sessions[org.id] != null
35645
+ rows.map((row) => ({
35646
+ "": row.active ? "*" : "",
35647
+ ...aliased ? { alias: row.alias } : {},
35648
+ name: row.name,
35649
+ id: row.id,
35650
+ ...multiRemote ? { remote: row.remote } : {},
35651
+ session: row.session
35279
35652
  })),
35280
- resolveFormat(flagValue(args, "-o", "--output"))
35653
+ resolved
35281
35654
  );
35282
35655
  };
35283
- var switchOrg = async (orgId) => {
35284
- if (orgIdOverride != null && orgIdOverride !== orgId) {
35656
+ var listOrgs = async (args) => {
35657
+ const rows = await collectOrgs();
35658
+ await cacheOrgNames(rows);
35659
+ printOrgs(rows, flagValue(args, "-o", "--output"));
35660
+ };
35661
+ var promptForOrg = async () => {
35662
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
35285
35663
  throw new Error(
35286
- `OVERAI_ORG_ID=${orgIdOverride} conflicts with the requested org ${orgId}. Unset it first.`
35664
+ "Name the org to switch to: overai org switch <alias|orgId|remote:orgId>"
35287
35665
  );
35666
+ const rows = await collectOrgs();
35667
+ if (rows.length === 0)
35668
+ throw new Error("No organizations found. Run overai signin first.");
35669
+ rows.forEach((row, index) => {
35670
+ process.stdout.write(
35671
+ `${String(index + 1).padStart(2)}. ${row.active ? "*" : " "} ${row.name || row.id} ${row.id} ${row.remote}${row.alias === "" ? "" : ` (${row.alias})`}
35672
+ `
35673
+ );
35674
+ });
35675
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
35676
+ try {
35677
+ const answer = await rl.question("Switch to: ");
35678
+ const chosen = rows[Number(answer.trim()) - 1];
35679
+ if (chosen == null) throw new Error(`No org numbered "${answer.trim()}".`);
35680
+ return { orgId: chosen.id, apiBaseUrl: chosen.apiBaseUrl };
35681
+ } finally {
35682
+ rl.close();
35288
35683
  }
35289
- setOrgScope(orgId);
35684
+ };
35685
+ var switchOrg = async (target) => {
35686
+ const override = orgOverride();
35687
+ const ref = await iife(async () => {
35688
+ if (target == null) return promptForOrg();
35689
+ const remote = remoteOnlyRef(target);
35690
+ if (remote == null) return resolveOrgRef(target);
35691
+ const orgId = readStore().deployments[remote]?.defaultOrgId;
35692
+ if (orgId == null)
35693
+ throw new Error(
35694
+ `No default org saved for ${target}. Name one: overai org switch ${target}:<orgId>`
35695
+ );
35696
+ return { orgId, apiBaseUrl: remote };
35697
+ });
35698
+ if (override != null && override.orgId !== ref.orgId) {
35699
+ throw new Error(
35700
+ `${override.source}=${override.orgId} conflicts with the requested org ${ref.orgId}. Drop it first.`
35701
+ );
35702
+ }
35703
+ setRemoteScope(ref.apiBaseUrl);
35704
+ setOrgScope(ref.orgId);
35290
35705
  try {
35291
35706
  await getAccessToken();
35292
35707
  } catch (error) {
35293
35708
  if (!(error instanceof InteractiveSignInRequired)) throw error;
35294
- await signIn(orgId);
35709
+ await signIn(ref.orgId);
35295
35710
  }
35296
35711
  await withStoreLock((store) => {
35297
- const deployment = store.deployments[apiBaseUrl()];
35298
- if (deployment?.sessions[orgId] == null)
35712
+ const deployment = store.deployments[ref.apiBaseUrl];
35713
+ if (deployment?.sessions[ref.orgId] == null)
35299
35714
  throw new Error(
35300
35715
  "Session was signed out before the default could be changed."
35301
35716
  );
35302
- deployment.defaultOrgId = orgId;
35717
+ deployment.defaultOrgId = ref.orgId;
35718
+ store.defaultApiBaseUrl = ref.apiBaseUrl;
35719
+ writeStore(store);
35720
+ });
35721
+ process.stdout.write(
35722
+ `Default org set to ${ref.orgId} on ${remoteName(ref.apiBaseUrl)}. Other org sessions are unchanged.
35723
+ This default is machine-global; a clone folder, --org, and OVERAI_ORG_ID all override it.
35724
+ `
35725
+ );
35726
+ };
35727
+ var aliasRows = (store) => Object.entries(store.aliases).map(([alias, ref]) => ({ alias, ref })).sort((a, b) => a.alias.localeCompare(b.alias));
35728
+ var aliasOrg = async (args) => {
35729
+ const format2 = flagValue(args, "-o", "--output");
35730
+ const [alias, target] = args.filter(
35731
+ (arg, index) => !arg.startsWith("-") && args[index - 1] !== "-o" && args[index - 1] !== "--output"
35732
+ );
35733
+ if (alias == null) {
35734
+ printResult(aliasRows(readStore()), resolveFormat(format2));
35735
+ return;
35736
+ }
35737
+ if (target == null)
35738
+ throw new Error("Usage: overai org alias <name> <alias|orgId|remote:orgId>");
35739
+ if (alias.includes(":") || /\s/.test(alias))
35740
+ throw new Error(`Alias "${alias}" cannot contain ":" or whitespace.`);
35741
+ const ref = resolveOrgRef(target);
35742
+ await withStoreLock((store) => {
35743
+ const name2 = remoteName(ref.apiBaseUrl, store);
35744
+ store.aliases[alias] = name2 === trimSlash(ref.apiBaseUrl) ? ref.orgId : `${name2}:${ref.orgId}`;
35745
+ writeStore(store);
35746
+ });
35747
+ process.stdout.write(
35748
+ `${alias} \u2192 ${ref.orgId} on ${remoteName(ref.apiBaseUrl)}
35749
+ `
35750
+ );
35751
+ };
35752
+ var unaliasOrg = async (alias) => {
35753
+ await withStoreLock((store) => {
35754
+ if (store.aliases[alias] == null)
35755
+ throw new Error(`No alias named "${alias}".`);
35756
+ store.aliases = omit(store.aliases, alias);
35757
+ writeStore(store);
35758
+ });
35759
+ process.stdout.write(`Removed alias ${alias}.
35760
+ `);
35761
+ };
35762
+
35763
+ // src/commands/remote.ts
35764
+ var listRemotes = (args) => {
35765
+ const store = readStore();
35766
+ const active = defaultApiBaseUrl(store);
35767
+ const rows = knownRemotes(store).map((remote) => {
35768
+ const deployment = store.deployments[remote.apiBaseUrl];
35769
+ return {
35770
+ active: remote.apiBaseUrl === active,
35771
+ name: remote.name,
35772
+ apiBaseUrl: remote.apiBaseUrl,
35773
+ appBaseUrl: deployment?.appBaseUrl ?? "",
35774
+ signedInOrgs: Object.keys(deployment?.sessions ?? {}).length
35775
+ };
35776
+ });
35777
+ const format2 = resolveFormat(flagValue(args, "-o", "--output"));
35778
+ if (format2 !== "table") {
35779
+ printResult(rows, format2);
35780
+ return;
35781
+ }
35782
+ const customApp = rows.some((row) => row.appBaseUrl !== "");
35783
+ printResult(
35784
+ rows.map((row) => ({
35785
+ "": row.active ? "*" : "",
35786
+ name: row.name,
35787
+ api: row.apiBaseUrl,
35788
+ ...customApp ? { app: row.appBaseUrl } : {},
35789
+ orgs: row.signedInOrgs === 0 ? "" : String(row.signedInOrgs)
35790
+ })),
35791
+ format2
35792
+ );
35793
+ };
35794
+ var addRemote = async (args) => {
35795
+ const [name2, url] = args;
35796
+ if (name2 == null || url == null)
35797
+ throw new Error(
35798
+ "Usage: overai remote add <name> <apiUrl> [--app <appUrl>]"
35799
+ );
35800
+ if (name2.includes(":") || /\s/.test(name2))
35801
+ throw new Error(`Remote name "${name2}" cannot contain ":" or whitespace.`);
35802
+ if (!url.startsWith("http://") && !url.startsWith("https://"))
35803
+ throw new Error(`"${url}" is not an http(s) URL.`);
35804
+ const apiBaseUrl2 = trimSlash(url);
35805
+ const app = flagValue(args, "--app");
35806
+ await withStoreLock((store) => {
35807
+ const existing = remoteUrl(name2, store);
35808
+ if (existing != null && existing !== apiBaseUrl2)
35809
+ throw new Error(
35810
+ `"${name2}" already names ${existing}. Remove that remote first, or choose another name.`
35811
+ );
35812
+ store.remotes[name2] = apiBaseUrl2;
35813
+ if (app != null) {
35814
+ const deployment = store.deployments[apiBaseUrl2] ??= {
35815
+ sessions: {},
35816
+ pending: {},
35817
+ signedOut: {}
35818
+ };
35819
+ deployment.appBaseUrl = trimSlash(app);
35820
+ }
35303
35821
  writeStore(store);
35304
35822
  });
35305
35823
  process.stdout.write(
35306
- `Default org set to ${orgId}. Other org sessions are unchanged.
35307
- This default is machine-global; workspace folders and OVERAI_ORG_ID override it.
35824
+ `Added remote ${name2} \u2192 ${apiBaseUrl2}. Sign in with: overai signin ${name2}
35308
35825
  `
35309
35826
  );
35310
35827
  };
35828
+ var removeRemote = async (name2) => {
35829
+ const apiBaseUrl2 = remoteUrl(name2, readStore());
35830
+ if (apiBaseUrl2 == null) throw new Error(`No remote named "${name2}".`);
35831
+ if (BUILT_IN_REMOTES[name2] != null)
35832
+ throw new Error(
35833
+ `"${name2}" is built in and cannot be removed. Run overai signout --all to drop its sessions.`
35834
+ );
35835
+ await withStoreLock(async (store) => {
35836
+ const deployment = store.deployments[apiBaseUrl2];
35837
+ if (Object.keys(deployment?.pending ?? {}).length > 0)
35838
+ throw new Error(
35839
+ `${name2} has an unfinished org exchange. Run overai signout --all first.`
35840
+ );
35841
+ const failures = [];
35842
+ for (const [orgId, session] of Object.entries(deployment?.sessions ?? {})) {
35843
+ try {
35844
+ await signOutHeadless(session.refreshToken, apiBaseUrl2);
35845
+ if (deployment != null)
35846
+ deployment.sessions = omit(deployment.sessions, orgId);
35847
+ } catch {
35848
+ failures.push(orgId);
35849
+ }
35850
+ }
35851
+ if (failures.length > 0) {
35852
+ writeStore(store);
35853
+ throw new Error(
35854
+ `Could not revoke ${name2} org ${failures.join(", ")}. Credentials retained; retry.`
35855
+ );
35856
+ }
35857
+ store.remotes = omit(store.remotes, name2);
35858
+ store.deployments = omit(store.deployments, apiBaseUrl2);
35859
+ store.aliases = Object.fromEntries(
35860
+ Object.entries(store.aliases).filter(
35861
+ ([, ref]) => !ref.startsWith(`${name2}:`)
35862
+ )
35863
+ );
35864
+ if (store.defaultApiBaseUrl === apiBaseUrl2) delete store.defaultApiBaseUrl;
35865
+ writeStore(store);
35866
+ });
35867
+ process.stdout.write(`Removed remote ${name2}.
35868
+ `);
35869
+ };
35870
+ var renameRemote = async (from, to) => {
35871
+ if (BUILT_IN_REMOTES[from] != null)
35872
+ throw new Error(`"${from}" is built in and cannot be renamed.`);
35873
+ if (to.includes(":") || /\s/.test(to))
35874
+ throw new Error(`Remote name "${to}" cannot contain ":" or whitespace.`);
35875
+ await withStoreLock((store) => {
35876
+ const url = remoteUrl(from, store);
35877
+ if (url == null) throw new Error(`No remote named "${from}".`);
35878
+ const taken = remoteUrl(to, store);
35879
+ if (taken != null)
35880
+ throw new Error(`"${to}" already names ${taken}. Choose another name.`);
35881
+ store.remotes = { ...omit(store.remotes, from), [to]: url };
35882
+ store.aliases = Object.fromEntries(
35883
+ Object.entries(store.aliases).map(([alias, ref]) => [
35884
+ alias,
35885
+ ref.startsWith(`${from}:`) ? `${to}:${ref.slice(from.length + 1)}` : ref
35886
+ ])
35887
+ );
35888
+ writeStore(store);
35889
+ });
35890
+ process.stdout.write(`Renamed remote ${from} to ${to}.
35891
+ `);
35892
+ };
35893
+ var runRemote = async (args) => {
35894
+ const [subcommand, ...rest] = args;
35895
+ switch (subcommand) {
35896
+ case void 0:
35897
+ case "list":
35898
+ listRemotes(rest);
35899
+ return;
35900
+ case "add":
35901
+ await addRemote(rest);
35902
+ return;
35903
+ case "remove":
35904
+ case "rm":
35905
+ if (rest[0] == null) throw new Error("Usage: overai remote remove <name>");
35906
+ await removeRemote(rest[0]);
35907
+ return;
35908
+ case "rename":
35909
+ if (rest[0] == null || rest[1] == null)
35910
+ throw new Error("Usage: overai remote rename <old> <new>");
35911
+ await renameRemote(rest[0], rest[1]);
35912
+ return;
35913
+ default:
35914
+ throw new Error(
35915
+ "Usage: overai remote list | add <name> <apiUrl> [--app <url>] | remove <name> | rename <old> <new>"
35916
+ );
35917
+ }
35918
+ };
35311
35919
 
35312
35920
  // src/commands/printablePage.ts
35313
35921
  var runPrintablePage = async (args) => {
@@ -35633,8 +36241,8 @@ var runDiff = async (args, clone) => {
35633
36241
  };
35634
36242
 
35635
36243
  // src/commands/recursiveSync.ts
35636
- var runRecursiveSync = async (command, args) => {
35637
- if (command === "push" && args.includes("--deploy"))
36244
+ var runRecursiveSync = async (command2, args) => {
36245
+ if (command2 === "push" && args.includes("--deploy"))
35638
36246
  throw new Error("Recursive push saves versions only. Deploy from an individual workspace.");
35639
36247
  const origin = openClone();
35640
36248
  const local = /* @__PURE__ */ new Map([[origin.manifest.workspaceId, origin]]);
@@ -35690,7 +36298,7 @@ var runRecursiveSync = async (command, args) => {
35690
36298
  process.stdout.write(`
35691
36299
  Workspace ${label} (${entry.workspace.id})
35692
36300
  `);
35693
- if (clone == null && command !== "pull") {
36301
+ if (clone == null && command2 !== "pull") {
35694
36302
  process.stdout.write("New remote workspace \u2014 run `overai pull --recursive` to clone.\n");
35695
36303
  continue;
35696
36304
  }
@@ -35702,9 +36310,9 @@ Workspace ${label} (${entry.workspace.id})
35702
36310
  throw new Error("Destination already exists, possibly from an incomplete clone. Local files kept; move the folder aside before retrying.");
35703
36311
  await runClone([entry.workspace.id, entry.root], true);
35704
36312
  available.add(entry.workspace.id);
35705
- } else if (command === "status") await runStatus(clone);
35706
- else if (command === "diff") await runDiff(args, clone);
35707
- else if (command === "pull") await runPull(clone);
36313
+ } else if (command2 === "status") await runStatus(clone);
36314
+ else if (command2 === "diff") await runDiff(args, clone);
36315
+ else if (command2 === "pull") await runPull(clone);
35708
36316
  else await runPush(args, clone);
35709
36317
  completed += 1;
35710
36318
  } catch (error) {
@@ -35714,9 +36322,9 @@ Workspace ${label} (${entry.workspace.id})
35714
36322
  }
35715
36323
  }
35716
36324
  process.stdout.write(`
35717
- Recursive ${command}: ${String(completed)} workspace(s) completed, ${String(failures.length)} failed or skipped.
36325
+ Recursive ${command2}: ${String(completed)} workspace(s) completed, ${String(failures.length)} failed or skipped.
35718
36326
  `);
35719
- if (failures.length > 0) throw new Error(`Recursive ${command} incomplete. Successful work was kept; resolve the reported issues and retry.`);
36327
+ if (failures.length > 0) throw new Error(`Recursive ${command2} incomplete. Successful work was kept; resolve the reported issues and retry.`);
35720
36328
  };
35721
36329
 
35722
36330
  // src/commands/update.ts
@@ -35732,6 +36340,11 @@ var packageRootUrl = () => {
35732
36340
  }
35733
36341
  throw new Error("package.json not found relative to the CLI bundle");
35734
36342
  };
36343
+ var compareVersions = (a, b) => {
36344
+ const av = a.split(".").map(Number);
36345
+ const bv = b.split(".").map(Number);
36346
+ return av.map((n, i) => n - (bv[i] ?? 0)).find((d) => d !== 0) ?? 0;
36347
+ };
35735
36348
  var cliVersion = () => {
35736
36349
  const pkg = JSON.parse(
35737
36350
  readFileSync8(new URL("package.json", packageRootUrl()), "utf8")
@@ -35771,9 +36384,7 @@ var runUpdate = async () => {
35771
36384
  `);
35772
36385
  return;
35773
36386
  }
35774
- const cur = current.split(".").map(Number);
35775
- const lat = latest.split(".").map(Number);
35776
- const cmp = cur.map((n, i) => n - (lat[i] ?? 0)).find((d) => d !== 0) ?? 0;
36387
+ const cmp = compareVersions(current, latest);
35777
36388
  if (cmp > 0) {
35778
36389
  process.stdout.write(
35779
36390
  `overai ${current} is ahead of latest (${latest}) \u2014 likely a @next install; nothing to do.
@@ -35975,10 +36586,13 @@ var platform2 = `# OverAI platform primer
35975
36586
  Saves reject a stale editing base without discarding your draft. Incorporate the latest saved changes before retrying; never retry with a newer base unless those changes have been reconciled. A save conflict does not deploy anything. Normal push sends the manifest base to an atomic server check. Only explicit --force omits it; never use --force as automatic conflict recovery.
35976
36587
 
35977
36588
  Organization context: Commands in a clone select its org automatically. Outside a clone,
35978
- use OVERAI_ORG_ID=<orgId>. Each backend/org has an independent saved session; org switch
35979
- changes only the machine-global default. Use whoami to inspect the effective scope.
35980
- If browser authorization is required, ask the user to run the suggested signin command.
35981
- Never change org to work around a clone-sync mismatch; use the correct workspace folder.
36589
+ put --org <ref> before the command (overai --org acme sql ...), or set OVERAI_ORG_ID for a
36590
+ whole shell. A ref is an org id, a local alias, or <remote>:<orgId>, and a named org
36591
+ 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
36593
+ saved session. Use whoami to inspect the effective scope. If browser authorization is
36594
+ required, ask the user to run the suggested signin command. Never change org to work
36595
+ around a clone-sync mismatch; use the correct workspace folder.
35982
36596
 
35983
36597
  Resources live in Workspaces. The runtime always executes the DEPLOYED version
35984
36598
  of a resource, never the latest \u2014 \`overai push\` creates a version, \`overai
@@ -36441,7 +37055,7 @@ var runReview = async () => {
36441
37055
  };
36442
37056
 
36443
37057
  // src/commands/sql.ts
36444
- import { createInterface } from "readline";
37058
+ import { createInterface as createInterface2 } from "readline";
36445
37059
 
36446
37060
  // src/commands/sqlCredential.ts
36447
37061
  var connectionString = async (tableSpaceId, secret) => {
@@ -36472,11 +37086,11 @@ var runSqlCredential = async (args) => {
36472
37086
  const tableSpaceId = id;
36473
37087
  const name2 = flagValue(args, "--name");
36474
37088
  if (!name2) throw new Error("create requires --name <name>");
36475
- const expiresIn = flagValue(args, "--expires-in");
36476
- if (expiresIn && args.includes("--never")) {
37089
+ const expiresIn2 = flagValue(args, "--expires-in");
37090
+ if (expiresIn2 && args.includes("--never")) {
36477
37091
  throw new Error("Use either --expires-in <days> or --never, not both");
36478
37092
  }
36479
- const days = expiresIn ? Number(expiresIn) : 365;
37093
+ const days = expiresIn2 ? Number(expiresIn2) : 365;
36480
37094
  if (!args.includes("--never") && (!Number.isInteger(days) || days < 1)) {
36481
37095
  throw new Error("--expires-in must be a positive whole number of days");
36482
37096
  }
@@ -36585,7 +37199,7 @@ transaction \u2014 BEGIN/SET/temp tables don't carry across statements.
36585
37199
  `;
36586
37200
  var runRepl = async (organizationId, tableSpaceId, format2) => {
36587
37201
  const interactive = process.stdin.isTTY;
36588
- const rl = createInterface({
37202
+ const rl = createInterface2({
36589
37203
  input: process.stdin,
36590
37204
  ...interactive ? { output: process.stdout, historySize: 1e3, terminal: true } : {}
36591
37205
  });
@@ -36834,17 +37448,101 @@ var runWorkspace = async (args) => {
36834
37448
  process.exitCode = 1;
36835
37449
  };
36836
37450
 
37451
+ // src/updateCheck.ts
37452
+ import { request as httpsRequest } from "https";
37453
+ import { existsSync as existsSync6 } from "fs";
37454
+ import { fileURLToPath as fileURLToPath2 } from "url";
37455
+ var CHECK_TIMEOUT_MS = 2e3;
37456
+ var fetchLatestVersion = (signal) => {
37457
+ return new Promise((resolve4, reject2) => {
37458
+ const req = httpsRequest(
37459
+ `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`,
37460
+ { signal, timeout: CHECK_TIMEOUT_MS },
37461
+ (res) => {
37462
+ if (res.statusCode !== 200) {
37463
+ res.resume();
37464
+ reject2(new Error(`registry responded HTTP ${String(res.statusCode)}`));
37465
+ return;
37466
+ }
37467
+ const chunks = [];
37468
+ res.on("data", (chunk) => chunks.push(chunk));
37469
+ res.on("end", () => {
37470
+ try {
37471
+ resolve4(JSON.parse(Buffer.concat(chunks).toString()));
37472
+ } catch {
37473
+ reject2(new Error("registry returned a non-JSON body"));
37474
+ }
37475
+ });
37476
+ }
37477
+ );
37478
+ req.on("timeout", () => {
37479
+ req.destroy(new Error("connect timed out"));
37480
+ });
37481
+ req.on("error", reject2);
37482
+ req.end();
37483
+ });
37484
+ };
37485
+ var PRINT_GRACE_MS = 500;
37486
+ var updateNotice = (current, latest) => {
37487
+ if (compareVersions(current, latest) >= 0) return null;
37488
+ return `overai ${current} \u2192 ${latest} available \u2014 run "overai update"`;
37489
+ };
37490
+ var startUpdateCheck = () => {
37491
+ const controller = new AbortController();
37492
+ const promise = (async () => {
37493
+ try {
37494
+ if (existsSync6(new URL("src", packageRootUrl()))) return null;
37495
+ const path = fileURLToPath2(import.meta.url);
37496
+ if (path.includes("_npx") || path.includes("dlx-")) return null;
37497
+ const body = await fetchLatestVersion(controller.signal);
37498
+ const latest = typeof body.version === "string" ? body.version : null;
37499
+ if (latest == null) return null;
37500
+ return updateNotice(cliVersion(), latest);
37501
+ } catch {
37502
+ return null;
37503
+ }
37504
+ })();
37505
+ return {
37506
+ promise,
37507
+ cancel: () => {
37508
+ controller.abort();
37509
+ }
37510
+ };
37511
+ };
37512
+ var printUpdateNotice = async (check) => {
37513
+ if (check == null) return;
37514
+ const grace = new Promise((resolve4) => {
37515
+ const timer = setTimeout(() => {
37516
+ resolve4(null);
37517
+ }, PRINT_GRACE_MS);
37518
+ timer.unref();
37519
+ });
37520
+ const message = await Promise.race([check.promise, grace]);
37521
+ check.cancel();
37522
+ if (message != null) process.stderr.write(`${message}
37523
+ `);
37524
+ };
37525
+
36837
37526
  // src/index.ts
36838
37527
  var HELP = `overai \u2014 OverAI command-line interface
36839
37528
 
36840
37529
  Usage:
36841
- overai signin Sign in via your browser; choose your
36842
- organization there to complete SSO
36843
- overai signout [--all] Revoke this org\u2019s saved CLI session;
37530
+ overai signin [<ref> | <apiUrl>] Sign in via your browser; choose your
37531
+ organization there to complete SSO.
37532
+ A URL signs in to that deployment and
37533
+ remembers it as a remote
37534
+ overai signout [<ref>] [--all] Revoke one org\u2019s saved CLI session;
36844
37535
  --all revokes all locally saved sessions
36845
- overai whoami Show the current identity and org
36846
- overai org list List your organizations
36847
- overai org switch <orgId> Select the default org
37536
+ overai whoami Show the current remote, identity, and org
37537
+ overai org list Your organizations, on every remote
37538
+ overai org switch [<ref>] Select the default org and remote;
37539
+ no argument picks from a list
37540
+ overai org alias [<name> <ref>] Name an org locally, or list the names
37541
+ overai org unalias <name>
37542
+ overai remote list Deployments you can reach
37543
+ overai remote add <name> <apiUrl> [--app <url>]
37544
+ overai remote remove <name> Forget it and revoke its sessions
37545
+ overai remote rename <old> <new>
36848
37546
  overai workspace list [--archived] List workspaces you can see in this org
36849
37547
  overai workspace show [workspaceId] Show a workspace's name and overview
36850
37548
  overai workspace users [workspaceId] List users and effective/direct roles
@@ -36932,17 +37630,21 @@ it from stdin. Prefer that for anything long or quote-heavy: an inline value is
36932
37630
  parsed by your shell first, and one apostrophe inside it silently truncates the
36933
37631
  rest. Write @@ to send a literal value that starts with "@".
36934
37632
 
36935
- Organizations: OVERAI_ORG_ID selects an org for one command or shell, else commands
36936
- use the clone\u2019s org (.overai/manifest.json), else the saved default. Each backend
36937
- and org has its own session. Entering another org preserves existing sessions;
36938
- org switch changes only the machine-global default. Clone sync commands reject
36939
- conflicting overrides. SSO prompts name the signin command to run explicitly.
36940
- whoami shows the backend, org, identity, and selection source.
37633
+ Organizations: an org is named by a ref \u2014 <orgId>, an alias, or <remote>:<orgId> \u2014
37634
+ wherever one is accepted. --org (before the command) selects one for a single
37635
+ invocation and OVERAI_ORG_ID for a shell; else commands use the clone\u2019s org
37636
+ (.overai/manifest.json), else the saved default. A named org picks its own
37637
+ remote, so concurrent agents in different orgs never need a second variable.
37638
+ Each remote and org has its own session; entering another org preserves the
37639
+ existing ones, and org switch changes only the machine-global default.
37640
+ Clone sync commands reject conflicting overrides. whoami shows the effective
37641
+ remote, org, identity, and what selected them.
36941
37642
 
36942
37643
  Output: table on a TTY, JSON when piped; -o json|table|csv to force.
36943
37644
  Aliases: login = signin, logout = signout, ws = workspace.
36944
37645
  Env: OVERAI_API_URL, OVERAI_APP_URL override the target deployment.
36945
37646
  OVERAI_ORG_ID overrides the organization for this shell.
37647
+ Global flags (before the command): --org <ref>
36946
37648
  `;
36947
37649
  process.stdout.on("error", (error) => {
36948
37650
  if (error.code === "EPIPE") process.exit(0);
@@ -36955,6 +37657,7 @@ var ORG_AGNOSTIC_COMMANDS = /* @__PURE__ */ new Set([
36955
37657
  "logout",
36956
37658
  "whoami",
36957
37659
  "org",
37660
+ "remote",
36958
37661
  "clone",
36959
37662
  "update",
36960
37663
  "upgrade",
@@ -36965,45 +37668,87 @@ var ORG_AGNOSTIC_COMMANDS = /* @__PURE__ */ new Set([
36965
37668
  "--help",
36966
37669
  "-h"
36967
37670
  ]);
37671
+ var SKIP_UPDATE_CHECK = /* @__PURE__ */ new Set([
37672
+ void 0,
37673
+ "update",
37674
+ "upgrade",
37675
+ "version",
37676
+ "--version",
37677
+ "-v",
37678
+ "help",
37679
+ "--help",
37680
+ "-h"
37681
+ ]);
37682
+ var withoutLeadingFlags = (argv) => {
37683
+ let index = 0;
37684
+ while (argv[index]?.startsWith("--") === true) {
37685
+ const flag = argv[index] ?? "";
37686
+ if (!LEADING_FLAGS.includes(flag)) break;
37687
+ if (argv[index + 1] == null) throw new Error(`${flag} needs a value.`);
37688
+ index += 2;
37689
+ }
37690
+ return argv.slice(index);
37691
+ };
36968
37692
  var run = async () => {
36969
- const [command, ...rest] = process.argv.slice(2);
36970
- if (command != null && !ORG_AGNOSTIC_COMMANDS.has(command)) {
37693
+ const [command2, ...rest] = withoutLeadingFlags(process.argv.slice(2));
37694
+ const misplaced = rest.find((arg) => LEADING_FLAGS.includes(arg));
37695
+ if (misplaced != null)
37696
+ throw new Error(
37697
+ `${misplaced} goes before the command: overai ${misplaced} <ref> ${command2 ?? "<command>"} ...`
37698
+ );
37699
+ if (command2 != null && !ORG_AGNOSTIC_COMMANDS.has(command2)) {
36971
37700
  pinOrgFromClone();
36972
37701
  announceOrgScope();
36973
37702
  }
36974
37703
  if (rest.includes("--recursive")) {
36975
- if (command !== "status" && command !== "diff" && command !== "pull" && command !== "push")
37704
+ if (command2 !== "status" && command2 !== "diff" && command2 !== "pull" && command2 !== "push")
36976
37705
  throw new Error(
36977
37706
  "--recursive supports status, diff, pull, and save-only push. Deploy and restore remain single-workspace."
36978
37707
  );
36979
37708
  await runRecursiveSync(
36980
- command,
37709
+ command2,
36981
37710
  rest.filter((arg) => arg !== "--recursive")
36982
37711
  );
36983
37712
  return;
36984
37713
  }
36985
- switch (command) {
37714
+ switch (command2) {
36986
37715
  case "signin":
36987
37716
  case "login":
36988
- await signIn();
37717
+ await signIn(rest.find((arg) => !arg.startsWith("-")));
36989
37718
  return;
36990
37719
  case "signout":
36991
37720
  case "logout":
36992
- await signOut(rest.includes("--all"));
37721
+ await signOut(
37722
+ rest.includes("--all"),
37723
+ rest.find((arg) => !arg.startsWith("-"))
37724
+ );
36993
37725
  return;
36994
37726
  case "whoami":
36995
- whoami();
37727
+ whoami(rest);
37728
+ return;
37729
+ case "remote":
37730
+ await runRemote(rest);
36996
37731
  return;
36997
37732
  case "org":
36998
37733
  if (rest[0] === "list") {
36999
37734
  await listOrgs(rest.slice(1));
37000
37735
  return;
37001
37736
  }
37002
- if (rest[0] === "switch" && rest[1] != null) {
37737
+ if (rest[0] === "switch") {
37003
37738
  await switchOrg(rest[1]);
37004
37739
  return;
37005
37740
  }
37006
- process.stderr.write("Usage: overai org list | org switch <orgId>\n");
37741
+ if (rest[0] === "alias") {
37742
+ await aliasOrg(rest.slice(1));
37743
+ return;
37744
+ }
37745
+ if (rest[0] === "unalias" && rest[1] != null) {
37746
+ await unaliasOrg(rest[1]);
37747
+ return;
37748
+ }
37749
+ process.stderr.write(
37750
+ "Usage: overai org list | org switch [<ref>] | org alias [<name> <ref>] | org unalias <name>\n"
37751
+ );
37007
37752
  process.exitCode = 1;
37008
37753
  return;
37009
37754
  case "workspace":
@@ -37108,16 +37853,23 @@ var run = async () => {
37108
37853
  process.stdout.write(HELP);
37109
37854
  return;
37110
37855
  default:
37111
- process.stderr.write(`Unknown command: ${command}
37856
+ process.stderr.write(`Unknown command: ${command2}
37112
37857
 
37113
37858
  ${HELP}`);
37114
37859
  process.exitCode = 1;
37115
37860
  }
37116
37861
  };
37862
+ var [parsedArgv] = resultOfSync(
37863
+ () => withoutLeadingFlags(process.argv.slice(2))
37864
+ );
37865
+ var [command] = parsedArgv ?? ["help"];
37866
+ var updateNotice2 = process.env.OVERAI_SKIP_UPDATE_CHECK === "1" || !process.stderr.isTTY || SKIP_UPDATE_CHECK.has(command) ? null : startUpdateCheck();
37117
37867
  run().catch((error) => {
37118
37868
  process.stderr.write(
37119
37869
  `${error instanceof Error ? error.message : String(error)}
37120
37870
  `
37121
37871
  );
37122
37872
  process.exitCode = 1;
37873
+ }).finally(() => {
37874
+ void printUpdateNotice(updateNotice2);
37123
37875
  });