@overaihq/cli 0.13.8 → 0.14.1

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,18 @@ 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 requireExplicitOrg = process.env.OVERAI_REQUIRE_EXPLICIT_ORG === "1";
26669
+ var orgRefOverride = leadingFlag("--org") ?? (process.env.OVERAI_ORG_ID != null && process.env.OVERAI_ORG_ID !== "" ? process.env.OVERAI_ORG_ID : void 0);
26670
+ var orgRefSource = leadingFlag("--org") != null ? "--org" : orgRefOverride != null ? "OVERAI_ORG_ID" : void 0;
26627
26671
  var appBaseUrl = (backend) => {
26628
26672
  const saved = readStore();
26629
26673
  return trimSlash(
@@ -26631,6 +26675,126 @@ var appBaseUrl = (backend) => {
26631
26675
  );
26632
26676
  };
26633
26677
 
26678
+ // src/remotes.ts
26679
+ var BUILT_IN_REMOTES = {
26680
+ prod: DEFAULT_API_BASE_URL,
26681
+ local: "http://localhost:3001"
26682
+ };
26683
+ var remoteUrl = (name2, store = readStore()) => {
26684
+ const url = store.remotes[name2] ?? BUILT_IN_REMOTES[name2];
26685
+ return url == null ? void 0 : trimSlash(url);
26686
+ };
26687
+ var knownRemotes = (store = readStore()) => {
26688
+ const byUrl = new Map(
26689
+ Object.entries({ ...BUILT_IN_REMOTES, ...store.remotes }).map(
26690
+ ([name2, url]) => [trimSlash(url), name2]
26691
+ )
26692
+ );
26693
+ for (const url of Object.keys(store.deployments)) {
26694
+ const trimmed = trimSlash(url);
26695
+ if (!byUrl.has(trimmed)) byUrl.set(trimmed, remoteName(trimmed, store));
26696
+ }
26697
+ return [...byUrl].map(([apiBaseUrl2, name2]) => ({ name: name2, apiBaseUrl: apiBaseUrl2 })).sort((a, b) => a.name.localeCompare(b.name));
26698
+ };
26699
+ var derivedRemoteName = (apiBaseUrl2) => {
26700
+ const [url] = resultOfSync(() => new URL(apiBaseUrl2));
26701
+ if (url == null) return void 0;
26702
+ if ((url.hostname === "localhost" || url.hostname === "127.0.0.1") && url.port === "3001")
26703
+ return "local";
26704
+ const match = /^api\.(?:([a-z0-9-]+)\.)?(?:overai|perygee)\.com$/.exec(
26705
+ url.hostname
26706
+ );
26707
+ if (match == null) return void 0;
26708
+ return match[1] ?? "prod";
26709
+ };
26710
+ var remoteName = (apiBaseUrl2, store = readStore()) => {
26711
+ const url = trimSlash(apiBaseUrl2);
26712
+ const registered = Object.entries(store.remotes).find(
26713
+ ([, value]) => trimSlash(value) === url
26714
+ );
26715
+ if (registered != null) return registered[0];
26716
+ const builtIn = Object.entries(BUILT_IN_REMOTES).find(
26717
+ ([, value]) => value === url
26718
+ );
26719
+ if (builtIn != null) return builtIn[0];
26720
+ const derived = derivedRemoteName(url);
26721
+ return derived != null && remoteUrl(derived, store) === url ? derived : url;
26722
+ };
26723
+ var defaultApiBaseUrl = (store = readStore()) => store.defaultApiBaseUrl ?? DEFAULT_API_BASE_URL;
26724
+ var remoteForOrg = (orgId, store = readStore()) => {
26725
+ const holders = Object.entries(store.deployments).filter(([, deployment]) => deployment.sessions[orgId] != null).map(([url]) => url);
26726
+ if (holders.length <= 1) return holders[0];
26727
+ const preferred = holders.find((url) => url === store.defaultApiBaseUrl);
26728
+ if (preferred != null) return preferred;
26729
+ throw new Error(
26730
+ `Org ${orgId} is signed in on ${holders.map((url) => remoteName(url, store)).join(" and ")}. Name one, e.g. --org ${remoteName(holders[0] ?? "", store)}:${orgId}.`
26731
+ );
26732
+ };
26733
+ var remoteOnlyRef = (text, store = readStore()) => {
26734
+ const trimmed = text.trim().replace(/:$/, "");
26735
+ if (store.aliases[trimmed] != null) return void 0;
26736
+ return remoteUrl(trimmed, store);
26737
+ };
26738
+ var parseOrgRef = (text, store = readStore()) => {
26739
+ const trimmed = text.trim();
26740
+ if (trimmed === "") throw new Error("Empty organization reference.");
26741
+ const expanded = store.aliases[trimmed] ?? trimmed;
26742
+ const colon = expanded.indexOf(":");
26743
+ if (colon === -1) return { orgId: expanded };
26744
+ const name2 = expanded.slice(0, colon);
26745
+ const orgPart = expanded.slice(colon + 1).trim();
26746
+ const apiBaseUrl2 = remoteUrl(name2, store);
26747
+ if (apiBaseUrl2 == null)
26748
+ throw new Error(
26749
+ `Unknown remote "${name2}". Known: ${knownRemotes(store).map((remote) => remote.name).join(", ")}. Add one with: overai remote add ${name2} <apiUrl>`
26750
+ );
26751
+ if (orgPart === "")
26752
+ throw new Error(`Missing org id in "${expanded}". Use ${name2}:<orgId>.`);
26753
+ const aliased = store.aliases[orgPart];
26754
+ const orgId = aliased == null ? orgPart : aliased.split(":").at(-1) ?? orgPart;
26755
+ return { orgId, apiBaseUrl: apiBaseUrl2 };
26756
+ };
26757
+ var resolveOrgRef = (text, store = readStore()) => {
26758
+ const ref = parseOrgRef(text, store);
26759
+ return {
26760
+ orgId: ref.orgId,
26761
+ apiBaseUrl: ref.apiBaseUrl ?? remoteForOrg(ref.orgId, store) ?? defaultApiBaseUrl(store)
26762
+ };
26763
+ };
26764
+ var cachedOverride;
26765
+ var orgOverride = () => {
26766
+ if (cachedOverride !== void 0) return cachedOverride;
26767
+ cachedOverride = orgRefOverride == null ? null : { ...parseOrgRef(orgRefOverride), source: orgRefSource ?? "--org" };
26768
+ return cachedOverride;
26769
+ };
26770
+ var orgRefText = (orgId, apiBaseUrl2, store = readStore()) => {
26771
+ const alias = Object.entries(store.aliases).find(([name3]) => {
26772
+ const [ref] = resultOfSync(() => resolveOrgRef(name3, store));
26773
+ return ref?.orgId === orgId && ref.apiBaseUrl === trimSlash(apiBaseUrl2);
26774
+ })?.[0];
26775
+ if (alias != null) return alias;
26776
+ if (trimSlash(apiBaseUrl2) === trimSlash(defaultApiBaseUrl(store)))
26777
+ return orgId;
26778
+ const name2 = remoteName(apiBaseUrl2, store);
26779
+ return name2 === trimSlash(apiBaseUrl2) ? orgId : `${name2}:${orgId}`;
26780
+ };
26781
+ var signinHint = (orgId, apiBaseUrl2) => {
26782
+ const store = readStore();
26783
+ const url = trimSlash(apiBaseUrl2);
26784
+ return remoteName(url, store) === url && url !== trimSlash(defaultApiBaseUrl(store)) ? `Run OVERAI_API_URL=${url} overai signin ${orgId}.` : `Run overai signin ${orgRefText(orgId, apiBaseUrl2, store)}.`;
26785
+ };
26786
+ var expiresIn = (at) => {
26787
+ if (at == null) return void 0;
26788
+ const ms = at - Date.now();
26789
+ if (ms <= 0) return "expired";
26790
+ const hours = Math.floor(ms / 36e5);
26791
+ if (hours < 1) return `${String(Math.max(1, Math.floor(ms / 6e4)))}m`;
26792
+ return hours < 48 ? `${String(hours)}h` : `${String(Math.floor(hours / 24))}d`;
26793
+ };
26794
+
26795
+ // ../common/src/stdlib/iife.ts
26796
+ var iife = (fn2) => fn2();
26797
+
26634
26798
  // ../common/src/stdlib/omit.ts
26635
26799
  var omit = (obj, ...keys) => {
26636
26800
  const result = { ...obj };
@@ -26640,7 +26804,29 @@ var omit = (obj, ...keys) => {
26640
26804
 
26641
26805
  // src/api.ts
26642
26806
  var commandApiBaseUrl;
26643
- var apiBaseUrl = () => commandApiBaseUrl ??= apiBaseUrlOverride ?? readStore().defaultApiBaseUrl ?? DEFAULT_API_BASE_URL;
26807
+ var explicitRemote;
26808
+ var cloneRemote;
26809
+ var setRemoteScope = (apiBaseUrl_) => {
26810
+ if (commandApiBaseUrl != null && commandApiBaseUrl !== apiBaseUrl_)
26811
+ throw new Error("Cannot change remote after a command has started.");
26812
+ explicitRemote = apiBaseUrl_;
26813
+ };
26814
+ var setCloneRemote = (apiBaseUrl_, organizationId) => {
26815
+ cloneRemote = { apiBaseUrl: apiBaseUrl_, organizationId };
26816
+ };
26817
+ var apiBaseUrl = () => commandApiBaseUrl ??= iife(() => {
26818
+ if (apiBaseUrlOverride != null) return apiBaseUrlOverride;
26819
+ if (explicitRemote != null) return explicitRemote;
26820
+ const store = readStore();
26821
+ const override = orgOverride();
26822
+ if (override?.apiBaseUrl != null) return override.apiBaseUrl;
26823
+ if (cloneRemote != null && (override == null || override.orgId === cloneRemote.organizationId))
26824
+ return cloneRemote.apiBaseUrl;
26825
+ const overrideHome = override == null ? void 0 : remoteForOrg(override.orgId, store);
26826
+ if (overrideHome != null) return overrideHome;
26827
+ if (cloneRemote != null) return cloneRemote.apiBaseUrl;
26828
+ return (orgScopeOverride == null ? void 0 : remoteForOrg(orgScopeOverride, store)) ?? defaultApiBaseUrl(store);
26829
+ });
26644
26830
  var AuthRpcError = class extends Error {
26645
26831
  constructor(status, message) {
26646
26832
  super(message);
@@ -26674,10 +26860,11 @@ var trpcMutation = async (procedure, input, baseUrl = apiBaseUrl()) => {
26674
26860
  return trpcResult(res, procedure);
26675
26861
  };
26676
26862
  var exchangeCliAuthCode = (code, codeVerifier) => trpcMutation("exchangeCliAuthCode", { code, codeVerifier });
26677
- var exchangeSessionToken = (refreshToken, destinationOrgId) => trpcMutation("exchangeSessionToken", {
26678
- refreshToken,
26679
- destinationOrgId
26680
- });
26863
+ var exchangeSessionToken = (refreshToken, destinationOrgId, backend = apiBaseUrl()) => trpcMutation(
26864
+ "exchangeSessionToken",
26865
+ { refreshToken, destinationOrgId },
26866
+ backend
26867
+ );
26681
26868
  var signOutHeadless = (refreshToken, baseUrl = apiBaseUrl()) => trpcMutation(
26682
26869
  "signOutHeadless",
26683
26870
  { refreshToken },
@@ -26711,7 +26898,7 @@ var context = () => {
26711
26898
  if (commandContext != null) return commandContext;
26712
26899
  const store = readStore();
26713
26900
  const deployment = store.deployments[apiBaseUrl()];
26714
- const organizationId = orgIdOverride ?? orgScopeOverride ?? deployment?.defaultOrgId;
26901
+ const organizationId = orgOverride()?.orgId ?? orgScopeOverride ?? deployment?.defaultOrgId;
26715
26902
  if (organizationId == null)
26716
26903
  throw new InteractiveSignInRequired("Not signed in. Run `overai signin`.");
26717
26904
  const session = deployment?.sessions[organizationId] ?? Object.values(deployment?.sessions ?? {})[0];
@@ -26732,11 +26919,12 @@ var scopedOrgId = () => {
26732
26919
  return null;
26733
26920
  }
26734
26921
  };
26922
+ var orgScopeSource = () => orgOverride()?.source ?? (orgScopeOverride != null ? ".overai/manifest.json" : "saved default");
26735
26923
  var announceOrgScope = () => {
26736
26924
  const org = scopedOrgId();
26737
26925
  if (org == null) return;
26738
26926
  process.stderr.write(
26739
- `Using org ${org} (${orgIdOverride != null ? "OVERAI_ORG_ID" : orgScopeOverride != null ? ".overai/manifest.json" : "saved default"}).
26927
+ `Using org ${org} on ${remoteName(apiBaseUrl())} (${orgScopeSource()}).
26740
26928
  `
26741
26929
  );
26742
26930
  };
@@ -26749,7 +26937,7 @@ var getAccessToken = async () => {
26749
26937
  "All CLI sessions were signed out in another process. Run the command again."
26750
26938
  );
26751
26939
  const deployment = store.deployments[baseUrl];
26752
- const signin = `Run OVERAI_ORG_ID=${target.organizationId} overai signin.`;
26940
+ const signin = signinHint(target.organizationId, baseUrl);
26753
26941
  if (deployment == null) throw new InteractiveSignInRequired(signin);
26754
26942
  if ((deployment.signedOut[target.organizationId] ?? 0) !== target.signedOut)
26755
26943
  throw new Error(`This org was signed out in another process. ${signin}`);
@@ -26815,6 +27003,7 @@ var getAccessToken = async () => {
26815
27003
  source.refreshToken = refreshed.refreshToken;
26816
27004
  source.accessToken = refreshed.token;
26817
27005
  source.accessTokenExpiresAt = Date.now() + refreshed.accessTokenValiditySeconds * 1e3;
27006
+ source.refreshTokenExpiresAt = refreshed.refreshTokenValiditySeconds == null ? void 0 : Date.now() + refreshed.refreshTokenValiditySeconds * 1e3;
26818
27007
  writeStore(store);
26819
27008
  }
26820
27009
  const sourceSessionId = decodeJwtClaims(source.accessToken).sid;
@@ -26875,9 +27064,11 @@ var getAccessToken = async () => {
26875
27064
  apiBaseUrl: baseUrl,
26876
27065
  identityId: claims.user_id,
26877
27066
  email: claims.email ?? target.email,
27067
+ organizationName: existing?.organizationName,
26878
27068
  refreshToken: result.refreshToken,
26879
27069
  accessToken: result.token,
26880
- accessTokenExpiresAt: Date.now() + result.accessTokenValiditySeconds * 1e3
27070
+ accessTokenExpiresAt: Date.now() + result.accessTokenValiditySeconds * 1e3,
27071
+ refreshTokenExpiresAt: result.refreshTokenValiditySeconds == null ? void 0 : Date.now() + result.refreshTokenValiditySeconds * 1e3
26881
27072
  };
26882
27073
  deployment.pending = omit(deployment.pending, target.organizationId);
26883
27074
  writeStore(store);
@@ -26885,6 +27076,34 @@ var getAccessToken = async () => {
26885
27076
  return { token: result.token, organizationId: target.organizationId };
26886
27077
  });
26887
27078
  };
27079
+ var accessTokenForSavedSession = async (backend, organizationId) => withStoreLock(async (store) => {
27080
+ const session = store.deployments[backend]?.sessions[organizationId];
27081
+ if (session == null)
27082
+ throw new InteractiveSignInRequired(
27083
+ signinHint(organizationId, backend)
27084
+ );
27085
+ if (session.accessToken != null && (session.accessTokenExpiresAt ?? 0) > Date.now() + 6e4)
27086
+ return session.accessToken;
27087
+ const result = await exchangeSessionToken(
27088
+ session.refreshToken,
27089
+ null,
27090
+ backend
27091
+ );
27092
+ if (result.type !== "success")
27093
+ throw new InteractiveSignInRequired(
27094
+ signinHint(organizationId, backend)
27095
+ );
27096
+ if (decodeJwtClaims(result.token).org !== organizationId)
27097
+ throw new Error(
27098
+ "The auth server returned a session for a different org or identity."
27099
+ );
27100
+ session.refreshToken = result.refreshToken;
27101
+ session.accessToken = result.token;
27102
+ session.accessTokenExpiresAt = Date.now() + result.accessTokenValiditySeconds * 1e3;
27103
+ session.refreshTokenExpiresAt = result.refreshTokenValiditySeconds == null ? void 0 : Date.now() + result.refreshTokenValiditySeconds * 1e3;
27104
+ writeStore(store);
27105
+ return result.token;
27106
+ });
26888
27107
  var v2Fetch = async (path, init) => {
26889
27108
  const auth = await getAccessToken();
26890
27109
  const headers = new Headers(init?.headers);
@@ -26914,7 +27133,7 @@ var v2Error = (label, res, rawBody) => {
26914
27133
  const claimed = (typeof field === "string" ? field : rawBody).trim().slice(0, 500);
26915
27134
  const reason = claimed.length === 0 || claimed === "Error" || claimed === "null" || claimed === "undefined" || claimed.toLowerCase() === STATUS_TEXT[res.status]?.toLowerCase() ? null : claimed;
26916
27135
  const orgHint = res.status === 404 || res.status === 403 ? `
26917
- If it lives in another org, prefix the command with OVERAI_ORG_ID=<orgId> (\`overai org list\` shows them).` : "";
27136
+ If it lives in another org, name that org: overai --org <ref> ... (\`overai org list\` shows them, \`overai org alias\` your shorthands).` : "";
26918
27137
  if (reason != null) return new Error(`${reason}${orgHint}`);
26919
27138
  const path = (() => {
26920
27139
  try {
@@ -26944,6 +27163,43 @@ import { randomBytes as randomBytes2 } from "crypto";
26944
27163
  import { createServer } from "http";
26945
27164
  import { hostname } from "os";
26946
27165
 
27166
+ // src/args.ts
27167
+ import { readFileSync as readFileSync2 } from "fs";
27168
+ var flagValue = (args, ...names) => {
27169
+ for (const name2 of names) {
27170
+ const i = args.indexOf(name2);
27171
+ if (i !== -1 && i + 1 < args.length) return args[i + 1];
27172
+ }
27173
+ return void 0;
27174
+ };
27175
+ var stdinClaimedBy = null;
27176
+ var readStdin = async (claimant = "stdin") => {
27177
+ if (stdinClaimedBy != null) {
27178
+ throw new Error(
27179
+ `Only one value can be read from stdin; ${stdinClaimedBy} already consumed it.`
27180
+ );
27181
+ }
27182
+ stdinClaimedBy = claimant;
27183
+ const chunks = [];
27184
+ for await (const chunk of process.stdin) {
27185
+ chunks.push(chunk);
27186
+ }
27187
+ return Buffer.concat(chunks).toString("utf8");
27188
+ };
27189
+ var readValue = async (value, label) => {
27190
+ if (value?.startsWith("@") !== true) return value;
27191
+ if (value.startsWith("@@")) return value.slice(1);
27192
+ const path = value.slice(1);
27193
+ if (path === "-") return (await readStdin(`${label} @-`)).trim();
27194
+ try {
27195
+ return readFileSync2(path, "utf8").trim();
27196
+ } catch {
27197
+ throw new Error(
27198
+ `${label}: could not read file "${path}". Write "@@" to send a literal value starting with "@".`
27199
+ );
27200
+ }
27201
+ };
27202
+
26947
27203
  // src/browser.ts
26948
27204
  import { spawn } from "child_process";
26949
27205
  import { platform } from "os";
@@ -26958,6 +27214,60 @@ var openBrowser = (url) => {
26958
27214
  }
26959
27215
  };
26960
27216
 
27217
+ // src/output.ts
27218
+ var resolveFormat = (explicit) => {
27219
+ if (explicit === "json" || explicit === "csv" || explicit === "table") {
27220
+ return explicit;
27221
+ }
27222
+ if (explicit != null) {
27223
+ throw new Error(
27224
+ `Unknown output format "${explicit}". Use json, table, or csv.`
27225
+ );
27226
+ }
27227
+ return process.stdout.isTTY ? "table" : "json";
27228
+ };
27229
+ var cell = (value) => {
27230
+ if (value == null) return "";
27231
+ if (typeof value === "string") return value;
27232
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
27233
+ return String(value);
27234
+ }
27235
+ return JSON.stringify(value);
27236
+ };
27237
+ var formatResult = (data, format2) => {
27238
+ if (format2 === "json" || !Array.isArray(data)) {
27239
+ return JSON.stringify(data, null, 2);
27240
+ }
27241
+ const records = data.map(
27242
+ (row) => row != null && typeof row === "object" ? row : { value: row }
27243
+ );
27244
+ if (records.length === 0) return "(no rows)";
27245
+ const columns = [...new Set(records.flatMap((r) => Object.keys(r)))];
27246
+ if (format2 === "csv") {
27247
+ const escape2 = (value) => {
27248
+ const text = cell(value);
27249
+ return /[",\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
27250
+ };
27251
+ return [
27252
+ columns.join(","),
27253
+ ...records.map((r) => columns.map((c) => escape2(r[c])).join(","))
27254
+ ].join("\n");
27255
+ }
27256
+ const widths = columns.map(
27257
+ (c) => Math.max(c.length, ...records.map((r) => cell(r[c]).length))
27258
+ );
27259
+ const line = (cells) => cells.map((text, i) => text.padEnd(widths[i] ?? 0)).join(" ");
27260
+ return [
27261
+ line(columns),
27262
+ line(widths.map((w) => "-".repeat(w))),
27263
+ ...records.map((r) => line(columns.map((c) => cell(r[c]))))
27264
+ ].join("\n");
27265
+ };
27266
+ var printResult = (data, format2) => {
27267
+ process.stdout.write(`${formatResult(data, format2)}
27268
+ `);
27269
+ };
27270
+
26961
27271
  // src/pkce.ts
26962
27272
  import { createHash, randomBytes } from "crypto";
26963
27273
  var createPkcePair = () => {
@@ -26967,7 +27277,7 @@ var createPkcePair = () => {
26967
27277
  };
26968
27278
 
26969
27279
  // src/workspace/manifest.ts
26970
- import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
27280
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
26971
27281
  import { dirname, join as join2, resolve } from "path";
26972
27282
  var MANIFEST_DIR = ".overai";
26973
27283
  var MANIFEST_FILE = "manifest.json";
@@ -26986,22 +27296,34 @@ var findWorkspaceRoot = (from = process.cwd()) => {
26986
27296
  }
26987
27297
  };
26988
27298
  var readManifest = (root) => JSON.parse(
26989
- readFileSync2(join2(root, MANIFEST_DIR, MANIFEST_FILE), "utf8")
27299
+ readFileSync3(join2(root, MANIFEST_DIR, MANIFEST_FILE), "utf8")
26990
27300
  );
26991
27301
  var openClone = () => {
26992
27302
  const root = findWorkspaceRoot();
26993
27303
  const manifest = readManifest(root);
26994
- if (orgIdOverride != null && orgIdOverride !== manifest.organizationId) {
27304
+ const override = orgOverride();
27305
+ if (override != null && override.orgId !== manifest.organizationId) {
27306
+ throw new Error(
27307
+ `${override.source}=${override.orgId} conflicts with this clone's org ${manifest.organizationId}. Drop it before syncing.`
27308
+ );
27309
+ }
27310
+ const namedRemote = override?.apiBaseUrl ?? apiBaseUrlOverride;
27311
+ if (namedRemote != null && manifest.apiBaseUrl != null && namedRemote !== manifest.apiBaseUrl) {
26995
27312
  throw new Error(
26996
- `OVERAI_ORG_ID=${orgIdOverride} conflicts with this clone's org ${manifest.organizationId}. Unset it before syncing.`
27313
+ `${override?.apiBaseUrl == null ? "OVERAI_API_URL" : override.source} names remote ${remoteName(namedRemote)}; this clone came from ${remoteName(manifest.apiBaseUrl)}.`
26997
27314
  );
26998
27315
  }
27316
+ if (manifest.apiBaseUrl != null)
27317
+ setCloneRemote(manifest.apiBaseUrl, manifest.organizationId);
26999
27318
  setOrgScope(manifest.organizationId);
27000
27319
  return { root, manifest };
27001
27320
  };
27002
27321
  var pinOrgFromClone = () => {
27003
27322
  try {
27004
- setOrgScope(readManifest(findWorkspaceRoot()).organizationId);
27323
+ const manifest = readManifest(findWorkspaceRoot());
27324
+ if (manifest.apiBaseUrl != null)
27325
+ setCloneRemote(manifest.apiBaseUrl, manifest.organizationId);
27326
+ setOrgScope(manifest.organizationId);
27005
27327
  } catch {
27006
27328
  }
27007
27329
  };
@@ -27022,14 +27344,14 @@ var writeSynced = (root, relPath, content) => {
27022
27344
  };
27023
27345
  var readWorkingFile = (root, relPath) => {
27024
27346
  try {
27025
- return readFileSync2(join2(root, relPath), "utf8");
27347
+ return readFileSync3(join2(root, relPath), "utf8");
27026
27348
  } catch {
27027
27349
  return null;
27028
27350
  }
27029
27351
  };
27030
27352
  var readBaseFile = (root, relPath) => {
27031
27353
  try {
27032
- return readFileSync2(basePath(root, relPath), "utf8");
27354
+ return readFileSync3(basePath(root, relPath), "utf8");
27033
27355
  } catch {
27034
27356
  return null;
27035
27357
  }
@@ -27116,11 +27438,28 @@ var startCallbackServer = (expectedState) => {
27116
27438
  });
27117
27439
  });
27118
27440
  };
27119
- var signIn = async (requestedOrgId = orgIdOverride) => {
27441
+ var signIn = async (target = orgRefOverride) => {
27442
+ const requestedOrgId = (() => {
27443
+ if (target == null) return void 0;
27444
+ if (target.startsWith("http://") || target.startsWith("https://")) {
27445
+ setRemoteScope(trimSlash(target));
27446
+ return orgOverride()?.orgId;
27447
+ }
27448
+ const remote = remoteOnlyRef(target);
27449
+ if (remote != null) {
27450
+ setRemoteScope(remote);
27451
+ return orgOverride()?.orgId;
27452
+ }
27453
+ const ref = parseOrgRef(target);
27454
+ if (ref.apiBaseUrl != null) setRemoteScope(ref.apiBaseUrl);
27455
+ return ref.orgId;
27456
+ })();
27120
27457
  const baseUrl = apiBaseUrl();
27121
27458
  const browserBaseUrl = appBaseUrl(baseUrl);
27122
27459
  if (browserBaseUrl === "")
27123
- throw new Error("Set OVERAI_APP_URL for this backend before signing in.");
27460
+ throw new Error(
27461
+ `No web address known for ${remoteName(baseUrl)}. Record one with: overai remote add <name> ${baseUrl} --app <appUrl>`
27462
+ );
27124
27463
  const initialStore = readStore();
27125
27464
  const { verifier, challenge } = createPkcePair();
27126
27465
  const state = randomBytes2(16).toString("base64url");
@@ -27183,12 +27522,16 @@ ${authorizeUrl}
27183
27522
  identityId: claims.user_id,
27184
27523
  email,
27185
27524
  accessToken: session.token,
27186
- accessTokenExpiresAt: Date.now() + session.accessTokenValiditySeconds * 1e3
27525
+ accessTokenExpiresAt: Date.now() + session.accessTokenValiditySeconds * 1e3,
27526
+ refreshTokenExpiresAt: Date.now() + session.refreshTokenValiditySeconds * 1e3
27187
27527
  };
27188
27528
  deployment.pending = omit(deployment.pending, session.organizationId);
27189
27529
  deployment.appBaseUrl = browserBaseUrl;
27190
27530
  deployment.defaultOrgId = session.organizationId;
27191
27531
  store.defaultApiBaseUrl = baseUrl;
27532
+ const derived = derivedRemoteName(baseUrl);
27533
+ if (derived != null && remoteUrl(derived, store) == null && !Object.values(store.remotes).some((url) => trimSlash(url) === baseUrl))
27534
+ store.remotes[derived] = baseUrl;
27192
27535
  writeStore(store);
27193
27536
  });
27194
27537
  } catch (error) {
@@ -27196,22 +27539,44 @@ ${authorizeUrl}
27196
27539
  throw error;
27197
27540
  }
27198
27541
  process.stdout.write(
27199
- `Signed in${email != null ? ` as ${email}` : ""} (org ${session.organizationId}).
27542
+ `Signed in${email != null ? ` as ${email}` : ""} to org ${session.organizationId} on ${remoteName(baseUrl)}.
27543
+ This is now the default for commands that don't name an org.
27200
27544
  `
27201
27545
  );
27202
27546
  } finally {
27203
27547
  close();
27204
27548
  }
27205
27549
  };
27206
- var signOut = async (all = false) => {
27207
- const root = (() => {
27208
- try {
27209
- return findWorkspaceRoot();
27210
- } catch {
27211
- return null;
27550
+ var signOut = async (all = false, target) => {
27551
+ if (target != null) {
27552
+ const ref = resolveOrgRef(target);
27553
+ const override = orgOverride();
27554
+ if (override != null && override.orgId !== ref.orgId) {
27555
+ throw new Error(
27556
+ `${override.source}=${override.orgId} conflicts with the requested org ${ref.orgId}. Drop it first.`
27557
+ );
27212
27558
  }
27213
- })();
27214
- if (root != null) setOrgScope(readManifest(root).organizationId);
27559
+ setRemoteScope(ref.apiBaseUrl);
27560
+ setOrgScope(ref.orgId);
27561
+ } else {
27562
+ const root = (() => {
27563
+ try {
27564
+ return findWorkspaceRoot();
27565
+ } catch {
27566
+ return null;
27567
+ }
27568
+ })();
27569
+ if (root != null) {
27570
+ const manifest = readManifest(root);
27571
+ if (manifest.apiBaseUrl != null)
27572
+ setCloneRemote(manifest.apiBaseUrl, manifest.organizationId);
27573
+ setOrgScope(manifest.organizationId);
27574
+ }
27575
+ }
27576
+ if (requireExplicitOrg && !all && orgScopeSource() === "saved default")
27577
+ throw new Error(
27578
+ "OVERAI_REQUIRE_EXPLICIT_ORG is set and nothing named an org to sign out of. Name one: overai signout <ref>, or revoke every saved session with: overai signout --all"
27579
+ );
27215
27580
  const baseUrl = apiBaseUrl();
27216
27581
  const orgId = scopedOrgId();
27217
27582
  await withStoreLock(async (store) => {
@@ -27265,11 +27630,11 @@ var signOut = async (all = false) => {
27265
27630
  );
27266
27631
  });
27267
27632
  process.stdout.write(
27268
- all ? "Signed out of all locally saved CLI sessions.\n" : `Signed out of org ${orgId ?? "(none selected)"}.
27633
+ all ? "Signed out of all locally saved CLI sessions.\n" : `Signed out of org ${orgId ?? "(none selected)"}${orgId == null ? "" : ` on ${remoteName(baseUrl)}`}.
27269
27634
  `
27270
27635
  );
27271
27636
  };
27272
- var whoami = () => {
27637
+ var whoami = (args = []) => {
27273
27638
  const root = (() => {
27274
27639
  try {
27275
27640
  return findWorkspaceRoot();
@@ -27277,108 +27642,40 @@ var whoami = () => {
27277
27642
  return null;
27278
27643
  }
27279
27644
  })();
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);
27313
- }
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);
27645
+ if (root != null) {
27646
+ const manifest = readManifest(root);
27647
+ if (manifest.apiBaseUrl != null)
27648
+ setCloneRemote(manifest.apiBaseUrl, manifest.organizationId);
27649
+ setOrgScope(manifest.organizationId);
27347
27650
  }
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");
27651
+ const store = readStore();
27652
+ const org = scopedOrgId();
27653
+ const backend = apiBaseUrl();
27654
+ const session = org == null ? void 0 : store.deployments[backend]?.sessions[org];
27655
+ const facts = {
27656
+ remote: remoteName(backend, store),
27657
+ apiBaseUrl: backend,
27658
+ org: org ?? "none",
27659
+ orgName: session?.organizationName ?? "",
27660
+ ref: org == null ? "" : orgRefText(org, backend, store),
27661
+ selectedBy: orgOverride()?.source ?? (root != null ? ".overai/manifest.json" : "saved default"),
27662
+ identity: session?.email ?? session?.identityId ?? "no saved session for this org",
27663
+ sessionExpires: expiresIn(session?.refreshTokenExpiresAt) ?? "unknown"
27664
+ };
27665
+ const format2 = resolveFormat(flagValue(args, "-o", "--output"));
27666
+ if (format2 !== "table") {
27667
+ printResult(facts, format2);
27668
+ return;
27368
27669
  }
27369
- const widths = columns.map(
27370
- (c) => Math.max(c.length, ...records.map((r) => cell(r[c]).length))
27670
+ process.stdout.write(
27671
+ [
27672
+ `Remote: ${facts.remote} (${facts.apiBaseUrl})`,
27673
+ `Org: ${facts.org}${facts.orgName === "" ? "" : ` (${facts.orgName})`}`,
27674
+ `Selected: ${facts.selectedBy}`,
27675
+ `Identity: ${facts.identity}`,
27676
+ `Session: ${facts.sessionExpires === "unknown" ? "expiry unknown" : facts.sessionExpires === "expired" ? "expired \u2014 sign in again" : `expires in ${facts.sessionExpires}`}`
27677
+ ].join("\n") + "\n"
27371
27678
  );
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
27679
  };
27383
27680
 
27384
27681
  // src/commands/agent.ts
@@ -27517,11 +27814,11 @@ var runAutomation = async (args) => {
27517
27814
  // src/workspace/validateElements.ts
27518
27815
  var findings = (parsed) => parsed.success ? [] : parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
27519
27816
  var validateAutomationElements = async (elements) => {
27520
- const { automationNodesSchema } = await import("./types-WI2WSJ3F.js");
27817
+ const { automationNodesSchema } = await import("./types-7JF7GZMJ.js");
27521
27818
  return findings(automationNodesSchema.safeParse(elements));
27522
27819
  };
27523
27820
  var validateWebPageConfig = async (config) => {
27524
- const { webPageConfigWriteSchema } = await import("./webPage-3MNCKCRX.js");
27821
+ const { webPageConfigWriteSchema } = await import("./webPage-7BXDGWX5.js");
27525
27822
  return findings(webPageConfigWriteSchema.safeParse(config));
27526
27823
  };
27527
27824
 
@@ -34257,6 +34554,7 @@ var runClone = async (args, isShallow = false) => {
34257
34554
  workspaceId: entry.workspace.id,
34258
34555
  workspaceName: entry.workspace.name,
34259
34556
  organizationId: entry.workspace.organizationId,
34557
+ apiBaseUrl: apiBaseUrl(),
34260
34558
  resources: {}
34261
34559
  },
34262
34560
  resourcesByAdapter
@@ -35130,6 +35428,7 @@ var ID_PREFIXES = {
35130
35428
  },
35131
35429
  snaptrade_user: { prefix: "stu", body: 12 },
35132
35430
  storage_transcription: { prefix: "stc", body: 10 },
35431
+ storage_transcription_job: { prefix: "stj", body: 10 },
35133
35432
  storage_vision_conversion: {
35134
35433
  prefix: "svc",
35135
35434
  body: 20,
@@ -35249,6 +35548,7 @@ var runOpen = async (args) => {
35249
35548
  };
35250
35549
 
35251
35550
  // src/commands/org.ts
35551
+ import { createInterface } from "readline/promises";
35252
35552
  var USER_ORGS_QUERY = `
35253
35553
  query UserOrganizations($identityId: String!) {
35254
35554
  identityOrganizations(condition: { identityId: $identityId }) {
@@ -35259,55 +35559,369 @@ var USER_ORGS_QUERY = `
35259
35559
  }
35260
35560
  }
35261
35561
  `;
35262
- var listOrgRows = async () => {
35263
- const auth = await getAccessToken();
35264
- const identityId = decodeJwtClaims(auth.token).user_id;
35265
- if (identityId == null) {
35562
+ var collectOrgs = async () => {
35563
+ const store = readStore();
35564
+ const aliasFor = (orgId, backend) => Object.entries(store.aliases).find(([alias]) => {
35565
+ const [ref] = resultOfSync(() => resolveOrgRef(alias, store));
35566
+ return ref?.orgId === orgId && trimSlash(ref.apiBaseUrl) === backend;
35567
+ })?.[0] ?? "";
35568
+ const activeBackend = defaultApiBaseUrl(store);
35569
+ const rows = await Promise.all(
35570
+ Object.entries(store.deployments).filter(([, deployment]) => Object.keys(deployment.sessions).length > 0).map(async ([backend, deployment]) => {
35571
+ const name2 = remoteName(backend, store);
35572
+ const asOrg = deployment.defaultOrgId != null && deployment.sessions[deployment.defaultOrgId] != null ? deployment.defaultOrgId : Object.keys(deployment.sessions)[0] ?? "";
35573
+ const [memberships, failure] = await fetchOrgNames(backend, asOrg).then(
35574
+ (names) => [names, void 0],
35575
+ (error) => [
35576
+ /* @__PURE__ */ new Map(),
35577
+ error instanceof Error ? error.message : String(error)
35578
+ ]
35579
+ );
35580
+ const ids = /* @__PURE__ */ new Set([
35581
+ ...Object.keys(deployment.sessions),
35582
+ ...memberships.keys()
35583
+ ]);
35584
+ return [...ids].map((id) => {
35585
+ const session = deployment.sessions[id];
35586
+ return {
35587
+ active: backend === activeBackend && deployment.defaultOrgId === id,
35588
+ alias: aliasFor(id, backend),
35589
+ name: memberships.get(id) ?? session?.organizationName ?? "",
35590
+ id,
35591
+ remote: name2,
35592
+ apiBaseUrl: backend,
35593
+ session: failure ?? (session == null ? "signin required" : expiresIn(session.refreshTokenExpiresAt) ?? "saved")
35594
+ };
35595
+ });
35596
+ })
35597
+ );
35598
+ return rows.flat().sort(
35599
+ (a, b) => a.remote.localeCompare(b.remote) || a.name.localeCompare(b.name) || a.id.localeCompare(b.id)
35600
+ );
35601
+ };
35602
+ var fetchOrgNames = async (backend, organizationId) => {
35603
+ const token = await accessTokenForSavedSession(backend, organizationId);
35604
+ const identityId = decodeJwtClaims(token).user_id;
35605
+ if (identityId == null)
35266
35606
  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);
35607
+ const response = await fetch(`${backend}/v2/graphql`, {
35608
+ method: "POST",
35609
+ headers: {
35610
+ "Content-Type": "application/json",
35611
+ Authorization: `Bearer ${token}`
35612
+ },
35613
+ body: JSON.stringify({
35614
+ query: USER_ORGS_QUERY,
35615
+ variables: { identityId }
35616
+ }),
35617
+ signal: AbortSignal.timeout(3e4)
35618
+ });
35619
+ const body = await response.json().catch(() => null);
35620
+ const failure = body?.errors?.[0]?.message;
35621
+ if (failure != null) throw new Error(failure);
35622
+ if (body?.data == null)
35623
+ throw new Error(`HTTP ${String(response.status)} listing organizations`);
35624
+ return new Map(
35625
+ body.data.identityOrganizations.nodes.filter((node) => node != null && !node.disabled).map((node) => node?.organization).filter((org) => org != null).map((org) => [org.id, org.name ?? ""])
35626
+ );
35270
35627
  };
35271
- var listOrgs = async (args) => {
35272
- const active = scopedOrgId();
35628
+ var cacheOrgNames = async (rows) => {
35629
+ const named = rows.filter((row) => row.name !== "");
35630
+ if (named.length === 0) return;
35631
+ await withStoreLock((store) => {
35632
+ let changed = false;
35633
+ for (const row of named) {
35634
+ const session = store.deployments[row.apiBaseUrl]?.sessions[row.id];
35635
+ if (session == null || session.organizationName === row.name) continue;
35636
+ session.organizationName = row.name;
35637
+ changed = true;
35638
+ }
35639
+ if (changed) writeStore(store);
35640
+ });
35641
+ };
35642
+ var printOrgs = (rows, format2) => {
35643
+ const resolved = resolveFormat(format2);
35644
+ if (resolved !== "table") {
35645
+ printResult(rows, resolved);
35646
+ return;
35647
+ }
35648
+ const multiRemote = new Set(rows.map((row) => row.remote)).size > 1;
35649
+ const aliased = rows.some((row) => row.alias !== "");
35273
35650
  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
35651
+ rows.map((row) => ({
35652
+ "": row.active ? "*" : "",
35653
+ ...aliased ? { alias: row.alias } : {},
35654
+ name: row.name,
35655
+ id: row.id,
35656
+ ...multiRemote ? { remote: row.remote } : {},
35657
+ session: row.session
35279
35658
  })),
35280
- resolveFormat(flagValue(args, "-o", "--output"))
35659
+ resolved
35281
35660
  );
35282
35661
  };
35283
- var switchOrg = async (orgId) => {
35284
- if (orgIdOverride != null && orgIdOverride !== orgId) {
35662
+ var listOrgs = async (args) => {
35663
+ const rows = await collectOrgs();
35664
+ await cacheOrgNames(rows);
35665
+ printOrgs(rows, flagValue(args, "-o", "--output"));
35666
+ };
35667
+ var promptForOrg = async () => {
35668
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
35285
35669
  throw new Error(
35286
- `OVERAI_ORG_ID=${orgIdOverride} conflicts with the requested org ${orgId}. Unset it first.`
35670
+ "Name the org to switch to: overai org switch <alias|orgId|remote:orgId>"
35671
+ );
35672
+ const rows = await collectOrgs();
35673
+ if (rows.length === 0)
35674
+ throw new Error("No organizations found. Run overai signin first.");
35675
+ rows.forEach((row, index) => {
35676
+ process.stdout.write(
35677
+ `${String(index + 1).padStart(2)}. ${row.active ? "*" : " "} ${row.name || row.id} ${row.id} ${row.remote}${row.alias === "" ? "" : ` (${row.alias})`}
35678
+ `
35287
35679
  );
35680
+ });
35681
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
35682
+ try {
35683
+ const answer = await rl.question("Switch to: ");
35684
+ const chosen = rows[Number(answer.trim()) - 1];
35685
+ if (chosen == null) throw new Error(`No org numbered "${answer.trim()}".`);
35686
+ return { orgId: chosen.id, apiBaseUrl: chosen.apiBaseUrl };
35687
+ } finally {
35688
+ rl.close();
35288
35689
  }
35289
- setOrgScope(orgId);
35690
+ };
35691
+ var switchOrg = async (target) => {
35692
+ const override = orgOverride();
35693
+ const ref = await iife(async () => {
35694
+ if (target == null) return promptForOrg();
35695
+ const remote = remoteOnlyRef(target);
35696
+ if (remote == null) return resolveOrgRef(target);
35697
+ const orgId = readStore().deployments[remote]?.defaultOrgId;
35698
+ if (orgId == null)
35699
+ throw new Error(
35700
+ `No default org saved for ${target}. Name one: overai org switch ${target}:<orgId>`
35701
+ );
35702
+ return { orgId, apiBaseUrl: remote };
35703
+ });
35704
+ if (override != null && override.orgId !== ref.orgId) {
35705
+ throw new Error(
35706
+ `${override.source}=${override.orgId} conflicts with the requested org ${ref.orgId}. Drop it first.`
35707
+ );
35708
+ }
35709
+ setRemoteScope(ref.apiBaseUrl);
35710
+ setOrgScope(ref.orgId);
35290
35711
  try {
35291
35712
  await getAccessToken();
35292
35713
  } catch (error) {
35293
35714
  if (!(error instanceof InteractiveSignInRequired)) throw error;
35294
- await signIn(orgId);
35715
+ await signIn(ref.orgId);
35295
35716
  }
35296
35717
  await withStoreLock((store) => {
35297
- const deployment = store.deployments[apiBaseUrl()];
35298
- if (deployment?.sessions[orgId] == null)
35718
+ const deployment = store.deployments[ref.apiBaseUrl];
35719
+ if (deployment?.sessions[ref.orgId] == null)
35299
35720
  throw new Error(
35300
35721
  "Session was signed out before the default could be changed."
35301
35722
  );
35302
- deployment.defaultOrgId = orgId;
35723
+ deployment.defaultOrgId = ref.orgId;
35724
+ store.defaultApiBaseUrl = ref.apiBaseUrl;
35303
35725
  writeStore(store);
35304
35726
  });
35305
35727
  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.
35728
+ `Default org set to ${ref.orgId} on ${remoteName(ref.apiBaseUrl)}. Other org sessions are unchanged.
35729
+ This default is machine-global; a clone folder, --org, and OVERAI_ORG_ID all override it.
35308
35730
  `
35309
35731
  );
35310
35732
  };
35733
+ var aliasRows = (store) => Object.entries(store.aliases).map(([alias, ref]) => ({ alias, ref })).sort((a, b) => a.alias.localeCompare(b.alias));
35734
+ var aliasOrg = async (args) => {
35735
+ const format2 = flagValue(args, "-o", "--output");
35736
+ const [alias, target] = args.filter(
35737
+ (arg, index) => !arg.startsWith("-") && args[index - 1] !== "-o" && args[index - 1] !== "--output"
35738
+ );
35739
+ if (alias == null) {
35740
+ printResult(aliasRows(readStore()), resolveFormat(format2));
35741
+ return;
35742
+ }
35743
+ if (target == null)
35744
+ throw new Error("Usage: overai org alias <name> <alias|orgId|remote:orgId>");
35745
+ if (alias.includes(":") || /\s/.test(alias))
35746
+ throw new Error(`Alias "${alias}" cannot contain ":" or whitespace.`);
35747
+ const ref = resolveOrgRef(target);
35748
+ await withStoreLock((store) => {
35749
+ const name2 = remoteName(ref.apiBaseUrl, store);
35750
+ store.aliases[alias] = name2 === trimSlash(ref.apiBaseUrl) ? ref.orgId : `${name2}:${ref.orgId}`;
35751
+ writeStore(store);
35752
+ });
35753
+ process.stdout.write(
35754
+ `${alias} \u2192 ${ref.orgId} on ${remoteName(ref.apiBaseUrl)}
35755
+ `
35756
+ );
35757
+ };
35758
+ var unaliasOrg = async (alias) => {
35759
+ await withStoreLock((store) => {
35760
+ if (store.aliases[alias] == null)
35761
+ throw new Error(`No alias named "${alias}".`);
35762
+ store.aliases = omit(store.aliases, alias);
35763
+ writeStore(store);
35764
+ });
35765
+ process.stdout.write(`Removed alias ${alias}.
35766
+ `);
35767
+ };
35768
+
35769
+ // src/commands/remote.ts
35770
+ var listRemotes = (args) => {
35771
+ const store = readStore();
35772
+ const active = defaultApiBaseUrl(store);
35773
+ const rows = knownRemotes(store).map((remote) => {
35774
+ const deployment = store.deployments[remote.apiBaseUrl];
35775
+ return {
35776
+ active: remote.apiBaseUrl === active,
35777
+ name: remote.name,
35778
+ apiBaseUrl: remote.apiBaseUrl,
35779
+ appBaseUrl: deployment?.appBaseUrl ?? "",
35780
+ signedInOrgs: Object.keys(deployment?.sessions ?? {}).length
35781
+ };
35782
+ });
35783
+ const format2 = resolveFormat(flagValue(args, "-o", "--output"));
35784
+ if (format2 !== "table") {
35785
+ printResult(rows, format2);
35786
+ return;
35787
+ }
35788
+ const customApp = rows.some((row) => row.appBaseUrl !== "");
35789
+ printResult(
35790
+ rows.map((row) => ({
35791
+ "": row.active ? "*" : "",
35792
+ name: row.name,
35793
+ api: row.apiBaseUrl,
35794
+ ...customApp ? { app: row.appBaseUrl } : {},
35795
+ orgs: row.signedInOrgs === 0 ? "" : String(row.signedInOrgs)
35796
+ })),
35797
+ format2
35798
+ );
35799
+ };
35800
+ var addRemote = async (args) => {
35801
+ const [name2, url] = args;
35802
+ if (name2 == null || url == null)
35803
+ throw new Error(
35804
+ "Usage: overai remote add <name> <apiUrl> [--app <appUrl>]"
35805
+ );
35806
+ if (name2.includes(":") || /\s/.test(name2))
35807
+ throw new Error(`Remote name "${name2}" cannot contain ":" or whitespace.`);
35808
+ if (!url.startsWith("http://") && !url.startsWith("https://"))
35809
+ throw new Error(`"${url}" is not an http(s) URL.`);
35810
+ const apiBaseUrl2 = trimSlash(url);
35811
+ const app = flagValue(args, "--app");
35812
+ await withStoreLock((store) => {
35813
+ const existing = remoteUrl(name2, store);
35814
+ if (existing != null && existing !== apiBaseUrl2)
35815
+ throw new Error(
35816
+ `"${name2}" already names ${existing}. Remove that remote first, or choose another name.`
35817
+ );
35818
+ store.remotes[name2] = apiBaseUrl2;
35819
+ if (app != null) {
35820
+ const deployment = store.deployments[apiBaseUrl2] ??= {
35821
+ sessions: {},
35822
+ pending: {},
35823
+ signedOut: {}
35824
+ };
35825
+ deployment.appBaseUrl = trimSlash(app);
35826
+ }
35827
+ writeStore(store);
35828
+ });
35829
+ process.stdout.write(
35830
+ `Added remote ${name2} \u2192 ${apiBaseUrl2}. Sign in with: overai signin ${name2}
35831
+ `
35832
+ );
35833
+ };
35834
+ var removeRemote = async (name2) => {
35835
+ const apiBaseUrl2 = remoteUrl(name2, readStore());
35836
+ if (apiBaseUrl2 == null) throw new Error(`No remote named "${name2}".`);
35837
+ if (BUILT_IN_REMOTES[name2] != null)
35838
+ throw new Error(
35839
+ `"${name2}" is built in and cannot be removed. Run overai signout --all to drop its sessions.`
35840
+ );
35841
+ await withStoreLock(async (store) => {
35842
+ const deployment = store.deployments[apiBaseUrl2];
35843
+ if (Object.keys(deployment?.pending ?? {}).length > 0)
35844
+ throw new Error(
35845
+ `${name2} has an unfinished org exchange. Run overai signout --all first.`
35846
+ );
35847
+ const failures = [];
35848
+ for (const [orgId, session] of Object.entries(deployment?.sessions ?? {})) {
35849
+ try {
35850
+ await signOutHeadless(session.refreshToken, apiBaseUrl2);
35851
+ if (deployment != null)
35852
+ deployment.sessions = omit(deployment.sessions, orgId);
35853
+ } catch {
35854
+ failures.push(orgId);
35855
+ }
35856
+ }
35857
+ if (failures.length > 0) {
35858
+ writeStore(store);
35859
+ throw new Error(
35860
+ `Could not revoke ${name2} org ${failures.join(", ")}. Credentials retained; retry.`
35861
+ );
35862
+ }
35863
+ store.remotes = omit(store.remotes, name2);
35864
+ store.deployments = omit(store.deployments, apiBaseUrl2);
35865
+ store.aliases = Object.fromEntries(
35866
+ Object.entries(store.aliases).filter(
35867
+ ([, ref]) => !ref.startsWith(`${name2}:`)
35868
+ )
35869
+ );
35870
+ if (store.defaultApiBaseUrl === apiBaseUrl2) delete store.defaultApiBaseUrl;
35871
+ writeStore(store);
35872
+ });
35873
+ process.stdout.write(`Removed remote ${name2}.
35874
+ `);
35875
+ };
35876
+ var renameRemote = async (from, to) => {
35877
+ if (BUILT_IN_REMOTES[from] != null)
35878
+ throw new Error(`"${from}" is built in and cannot be renamed.`);
35879
+ if (to.includes(":") || /\s/.test(to))
35880
+ throw new Error(`Remote name "${to}" cannot contain ":" or whitespace.`);
35881
+ await withStoreLock((store) => {
35882
+ const url = remoteUrl(from, store);
35883
+ if (url == null) throw new Error(`No remote named "${from}".`);
35884
+ const taken = remoteUrl(to, store);
35885
+ if (taken != null)
35886
+ throw new Error(`"${to}" already names ${taken}. Choose another name.`);
35887
+ store.remotes = { ...omit(store.remotes, from), [to]: url };
35888
+ store.aliases = Object.fromEntries(
35889
+ Object.entries(store.aliases).map(([alias, ref]) => [
35890
+ alias,
35891
+ ref.startsWith(`${from}:`) ? `${to}:${ref.slice(from.length + 1)}` : ref
35892
+ ])
35893
+ );
35894
+ writeStore(store);
35895
+ });
35896
+ process.stdout.write(`Renamed remote ${from} to ${to}.
35897
+ `);
35898
+ };
35899
+ var runRemote = async (args) => {
35900
+ const [subcommand, ...rest] = args;
35901
+ switch (subcommand) {
35902
+ case void 0:
35903
+ case "list":
35904
+ listRemotes(rest);
35905
+ return;
35906
+ case "add":
35907
+ await addRemote(rest);
35908
+ return;
35909
+ case "remove":
35910
+ case "rm":
35911
+ if (rest[0] == null) throw new Error("Usage: overai remote remove <name>");
35912
+ await removeRemote(rest[0]);
35913
+ return;
35914
+ case "rename":
35915
+ if (rest[0] == null || rest[1] == null)
35916
+ throw new Error("Usage: overai remote rename <old> <new>");
35917
+ await renameRemote(rest[0], rest[1]);
35918
+ return;
35919
+ default:
35920
+ throw new Error(
35921
+ "Usage: overai remote list | add <name> <apiUrl> [--app <url>] | remove <name> | rename <old> <new>"
35922
+ );
35923
+ }
35924
+ };
35311
35925
 
35312
35926
  // src/commands/printablePage.ts
35313
35927
  var runPrintablePage = async (args) => {
@@ -35978,10 +36592,17 @@ var platform2 = `# OverAI platform primer
35978
36592
  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.
35979
36593
 
35980
36594
  Organization context: Commands in a clone select its org automatically. Outside a clone,
35981
- use OVERAI_ORG_ID=<orgId>. Each backend/org has an independent saved session; org switch
35982
- changes only the machine-global default. Use whoami to inspect the effective scope.
35983
- If browser authorization is required, ask the user to run the suggested signin command.
35984
- Never change org to work around a clone-sync mismatch; use the correct workspace folder.
36595
+ put --org <ref> before the command (overai --org acme sql ...), or set OVERAI_ORG_ID for a
36596
+ whole shell. A ref is an org id, a local alias, or <remote>:<orgId>, and a named org
36597
+ selects its own deployment \u2014 never switch the machine-global default to reach another org,
36598
+ because every other session on the machine shares it. Prefer naming the org on every
36599
+ command rather than relying on the saved default: the default is whatever another session
36600
+ last switched to, so falling back to it reads the wrong tenant instead of failing.
36601
+ OVERAI_REQUIRE_EXPLICIT_ORG=1 turns that fallback into an error for every command
36602
+ that acts on an org. Each remote/org has an independent
36603
+ saved session. Use whoami to inspect the effective scope. If browser authorization is
36604
+ required, ask the user to run the suggested signin command. Never change org to work
36605
+ around a clone-sync mismatch; use the correct workspace folder.
35985
36606
 
35986
36607
  Resources live in Workspaces. The runtime always executes the DEPLOYED version
35987
36608
  of a resource, never the latest \u2014 \`overai push\` creates a version, \`overai
@@ -36444,7 +37065,7 @@ var runReview = async () => {
36444
37065
  };
36445
37066
 
36446
37067
  // src/commands/sql.ts
36447
- import { createInterface } from "readline";
37068
+ import { createInterface as createInterface2 } from "readline";
36448
37069
 
36449
37070
  // src/commands/sqlCredential.ts
36450
37071
  var connectionString = async (tableSpaceId, secret) => {
@@ -36475,11 +37096,11 @@ var runSqlCredential = async (args) => {
36475
37096
  const tableSpaceId = id;
36476
37097
  const name2 = flagValue(args, "--name");
36477
37098
  if (!name2) throw new Error("create requires --name <name>");
36478
- const expiresIn = flagValue(args, "--expires-in");
36479
- if (expiresIn && args.includes("--never")) {
37099
+ const expiresIn2 = flagValue(args, "--expires-in");
37100
+ if (expiresIn2 && args.includes("--never")) {
36480
37101
  throw new Error("Use either --expires-in <days> or --never, not both");
36481
37102
  }
36482
- const days = expiresIn ? Number(expiresIn) : 365;
37103
+ const days = expiresIn2 ? Number(expiresIn2) : 365;
36483
37104
  if (!args.includes("--never") && (!Number.isInteger(days) || days < 1)) {
36484
37105
  throw new Error("--expires-in must be a positive whole number of days");
36485
37106
  }
@@ -36588,7 +37209,7 @@ transaction \u2014 BEGIN/SET/temp tables don't carry across statements.
36588
37209
  `;
36589
37210
  var runRepl = async (organizationId, tableSpaceId, format2) => {
36590
37211
  const interactive = process.stdin.isTTY;
36591
- const rl = createInterface({
37212
+ const rl = createInterface2({
36592
37213
  input: process.stdin,
36593
37214
  ...interactive ? { output: process.stdout, historySize: 1e3, terminal: true } : {}
36594
37215
  });
@@ -36916,13 +37537,22 @@ var printUpdateNotice = async (check) => {
36916
37537
  var HELP = `overai \u2014 OverAI command-line interface
36917
37538
 
36918
37539
  Usage:
36919
- overai signin Sign in via your browser; choose your
36920
- organization there to complete SSO
36921
- overai signout [--all] Revoke this org\u2019s saved CLI session;
37540
+ overai signin [<ref> | <apiUrl>] Sign in via your browser; choose your
37541
+ organization there to complete SSO.
37542
+ A URL signs in to that deployment and
37543
+ remembers it as a remote
37544
+ overai signout [<ref>] [--all] Revoke one org\u2019s saved CLI session;
36922
37545
  --all revokes all locally saved sessions
36923
- overai whoami Show the current identity and org
36924
- overai org list List your organizations
36925
- overai org switch <orgId> Select the default org
37546
+ overai whoami Show the current remote, identity, and org
37547
+ overai org list Your organizations, on every remote
37548
+ overai org switch [<ref>] Select the default org and remote;
37549
+ no argument picks from a list
37550
+ overai org alias [<name> <ref>] Name an org locally, or list the names
37551
+ overai org unalias <name>
37552
+ overai remote list Deployments you can reach
37553
+ overai remote add <name> <apiUrl> [--app <url>]
37554
+ overai remote remove <name> Forget it and revoke its sessions
37555
+ overai remote rename <old> <new>
36926
37556
  overai workspace list [--archived] List workspaces you can see in this org
36927
37557
  overai workspace show [workspaceId] Show a workspace's name and overview
36928
37558
  overai workspace users [workspaceId] List users and effective/direct roles
@@ -37010,17 +37640,24 @@ it from stdin. Prefer that for anything long or quote-heavy: an inline value is
37010
37640
  parsed by your shell first, and one apostrophe inside it silently truncates the
37011
37641
  rest. Write @@ to send a literal value that starts with "@".
37012
37642
 
37013
- Organizations: OVERAI_ORG_ID selects an org for one command or shell, else commands
37014
- use the clone\u2019s org (.overai/manifest.json), else the saved default. Each backend
37015
- and org has its own session. Entering another org preserves existing sessions;
37016
- org switch changes only the machine-global default. Clone sync commands reject
37017
- conflicting overrides. SSO prompts name the signin command to run explicitly.
37018
- whoami shows the backend, org, identity, and selection source.
37643
+ Organizations: an org is named by a ref \u2014 <orgId>, an alias, or <remote>:<orgId> \u2014
37644
+ wherever one is accepted. --org (before the command) selects one for a single
37645
+ invocation and OVERAI_ORG_ID for a shell; else commands use the clone\u2019s org
37646
+ (.overai/manifest.json), else the saved default. A named org picks its own
37647
+ remote, so concurrent agents in different orgs never need a second variable.
37648
+ Each remote and org has its own session; entering another org preserves the
37649
+ existing ones, and org switch changes only the machine-global default.
37650
+ Clone sync commands reject conflicting overrides. whoami shows the effective
37651
+ remote, org, identity, and what selected them.
37019
37652
 
37020
37653
  Output: table on a TTY, JSON when piped; -o json|table|csv to force.
37021
37654
  Aliases: login = signin, logout = signout, ws = workspace.
37022
37655
  Env: OVERAI_API_URL, OVERAI_APP_URL override the target deployment.
37023
37656
  OVERAI_ORG_ID overrides the organization for this shell.
37657
+ OVERAI_REQUIRE_EXPLICIT_ORG=1 refuses to fall back to the saved default
37658
+ org, so every command that acts on one must name it with --org or stand in
37659
+ a clone.
37660
+ Global flags (before the command): --org <ref>
37024
37661
  `;
37025
37662
  process.stdout.on("error", (error) => {
37026
37663
  if (error.code === "EPIPE") process.exit(0);
@@ -37033,6 +37670,7 @@ var ORG_AGNOSTIC_COMMANDS = /* @__PURE__ */ new Set([
37033
37670
  "logout",
37034
37671
  "whoami",
37035
37672
  "org",
37673
+ "remote",
37036
37674
  "clone",
37037
37675
  "update",
37038
37676
  "upgrade",
@@ -37043,6 +37681,24 @@ var ORG_AGNOSTIC_COMMANDS = /* @__PURE__ */ new Set([
37043
37681
  "--help",
37044
37682
  "-h"
37045
37683
  ]);
37684
+ var ORG_OPTIONAL_COMMANDS = /* @__PURE__ */ new Set([
37685
+ "signin",
37686
+ "login",
37687
+ "signout",
37688
+ "logout",
37689
+ "whoami",
37690
+ "org",
37691
+ "remote",
37692
+ "best-practices",
37693
+ "update",
37694
+ "upgrade",
37695
+ "version",
37696
+ "--version",
37697
+ "-v",
37698
+ "help",
37699
+ "--help",
37700
+ "-h"
37701
+ ]);
37046
37702
  var SKIP_UPDATE_CHECK = /* @__PURE__ */ new Set([
37047
37703
  void 0,
37048
37704
  "update",
@@ -37054,12 +37710,30 @@ var SKIP_UPDATE_CHECK = /* @__PURE__ */ new Set([
37054
37710
  "--help",
37055
37711
  "-h"
37056
37712
  ]);
37713
+ var withoutLeadingFlags = (argv) => {
37714
+ let index = 0;
37715
+ while (argv[index]?.startsWith("--") === true) {
37716
+ const flag = argv[index] ?? "";
37717
+ if (!LEADING_FLAGS.includes(flag)) break;
37718
+ if (argv[index + 1] == null) throw new Error(`${flag} needs a value.`);
37719
+ index += 2;
37720
+ }
37721
+ return argv.slice(index);
37722
+ };
37057
37723
  var run = async () => {
37058
- const [command2, ...rest] = process.argv.slice(2);
37059
- if (command2 != null && !ORG_AGNOSTIC_COMMANDS.has(command2)) {
37060
- pinOrgFromClone();
37061
- announceOrgScope();
37062
- }
37724
+ const [command2, ...rest] = withoutLeadingFlags(process.argv.slice(2));
37725
+ const misplaced = rest.find((arg) => LEADING_FLAGS.includes(arg));
37726
+ if (misplaced != null)
37727
+ throw new Error(
37728
+ `${misplaced} goes before the command: overai ${misplaced} <ref> ${command2 ?? "<command>"} ...`
37729
+ );
37730
+ const orgFromFolder = command2 != null && !ORG_AGNOSTIC_COMMANDS.has(command2);
37731
+ if (orgFromFolder) pinOrgFromClone();
37732
+ if (requireExplicitOrg && command2 != null && !ORG_OPTIONAL_COMMANDS.has(command2) && orgScopeSource() === "saved default")
37733
+ throw new Error(
37734
+ `OVERAI_REQUIRE_EXPLICIT_ORG is set and nothing named an org for "${command2}". Put --org <ref> before the command, or run it inside a clone. See your shorthands with: overai org alias`
37735
+ );
37736
+ if (orgFromFolder) announceOrgScope();
37063
37737
  if (rest.includes("--recursive")) {
37064
37738
  if (command2 !== "status" && command2 !== "diff" && command2 !== "pull" && command2 !== "push")
37065
37739
  throw new Error(
@@ -37074,25 +37748,41 @@ var run = async () => {
37074
37748
  switch (command2) {
37075
37749
  case "signin":
37076
37750
  case "login":
37077
- await signIn();
37751
+ await signIn(rest.find((arg) => !arg.startsWith("-")));
37078
37752
  return;
37079
37753
  case "signout":
37080
37754
  case "logout":
37081
- await signOut(rest.includes("--all"));
37755
+ await signOut(
37756
+ rest.includes("--all"),
37757
+ rest.find((arg) => !arg.startsWith("-"))
37758
+ );
37082
37759
  return;
37083
37760
  case "whoami":
37084
- whoami();
37761
+ whoami(rest);
37762
+ return;
37763
+ case "remote":
37764
+ await runRemote(rest);
37085
37765
  return;
37086
37766
  case "org":
37087
37767
  if (rest[0] === "list") {
37088
37768
  await listOrgs(rest.slice(1));
37089
37769
  return;
37090
37770
  }
37091
- if (rest[0] === "switch" && rest[1] != null) {
37771
+ if (rest[0] === "switch") {
37092
37772
  await switchOrg(rest[1]);
37093
37773
  return;
37094
37774
  }
37095
- process.stderr.write("Usage: overai org list | org switch <orgId>\n");
37775
+ if (rest[0] === "alias") {
37776
+ await aliasOrg(rest.slice(1));
37777
+ return;
37778
+ }
37779
+ if (rest[0] === "unalias" && rest[1] != null) {
37780
+ await unaliasOrg(rest[1]);
37781
+ return;
37782
+ }
37783
+ process.stderr.write(
37784
+ "Usage: overai org list | org switch [<ref>] | org alias [<name> <ref>] | org unalias <name>\n"
37785
+ );
37096
37786
  process.exitCode = 1;
37097
37787
  return;
37098
37788
  case "workspace":
@@ -37203,7 +37893,10 @@ ${HELP}`);
37203
37893
  process.exitCode = 1;
37204
37894
  }
37205
37895
  };
37206
- var [command] = process.argv.slice(2);
37896
+ var [parsedArgv] = resultOfSync(
37897
+ () => withoutLeadingFlags(process.argv.slice(2))
37898
+ );
37899
+ var [command] = parsedArgv ?? ["help"];
37207
37900
  var updateNotice2 = process.env.OVERAI_SKIP_UPDATE_CHECK === "1" || !process.stderr.isTTY || SKIP_UPDATE_CHECK.has(command) ? null : startUpdateCheck();
37208
37901
  run().catch((error) => {
37209
37902
  process.stderr.write(