@overaihq/cli 0.10.3 → 0.11.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.
Files changed (3) hide show
  1. package/README.md +12 -1
  2. package/dist/index.js +87 -35
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -20,7 +20,7 @@ overai workspace list # find a workspace id
20
20
  overai workspace show <id> # read its name and overview
21
21
  overai workspace users <id> # list users and direct/inherited roles
22
22
  overai create agent --workspace <id> --name "Support agent"
23
- overai clone <workspaceId> # materialize resources and WORKSPACE.md
23
+ overai clone <workspaceId> # clone the workspace and its descendants
24
24
  # ...edit files...
25
25
  overai status # local edits + remote drift
26
26
  overai diff # what changed
@@ -36,6 +36,17 @@ Run `overai help` for the full command list, including function invocation,
36
36
  automation runs and per-node state, tablespace SQL (a psql-style REPL), and
37
37
  `overai dev` for live-previewing a web page from local files.
38
38
 
39
+ Clone includes accessible, non-archived descendants in nested
40
+ `workspaces/<slug>.<workspaceId>/` folders. Each workspace has its own
41
+ `.overai/manifest.json`, resource files, and `WORKSPACE.md`. Existing resource
42
+ filenames and the top-level clone directory stay unchanged.
43
+
44
+ Commands use the nearest workspace folder: `cd` into a child to edit or sync it.
45
+ `status`, `pull`, `push`, `deploy`, and `restore` still affect only that workspace,
46
+ not its descendants. Pull does not discover new child workspaces. Clone those
47
+ separately, or clone the parent into a fresh directory. Local file moves never
48
+ move resources between workspaces in OverAI.
49
+
39
50
  Inside a clone, `workspace show` and `workspace users` infer the workspace id.
40
51
  `WORKSPACE.md` is the overview captured when the clone is created; use
41
52
  `workspace show` when you need the current server value.
package/dist/index.js CHANGED
@@ -33845,6 +33845,7 @@ import { dirname as dirname3, join as join5 } from "path";
33845
33845
  // src/commands/sync.ts
33846
33846
  import {
33847
33847
  existsSync as existsSync3,
33848
+ lstatSync,
33848
33849
  mkdirSync as mkdirSync3,
33849
33850
  readFileSync as readFileSync6,
33850
33851
  unlinkSync,
@@ -33981,43 +33982,91 @@ var runClone = async (args) => {
33981
33982
  if (existsSync3(join4(root, "WORKSPACE.md")) && partialCloneWorkspaceId == null) {
33982
33983
  throw new Error(`${root} already contains WORKSPACE.md.`);
33983
33984
  }
33984
- const resourcesByAdapter = [];
33985
- for (const adapter of allAdapters) {
33986
- resourcesByAdapter.push({
33987
- adapter,
33988
- resources: await adapter.fetchAll(workspaceId)
33989
- });
33990
- }
33991
- mkdirSync3(join4(root, ".overai"), { recursive: true });
33992
- writeFileSync4(cloneMarker, `${workspaceId}
33985
+ const pending = [{ workspace, root }];
33986
+ const seen = /* @__PURE__ */ new Set([workspace.id]);
33987
+ const plan = [];
33988
+ for (const entry of pending) {
33989
+ const marker = join4(entry.root, ".overai", "clone-in-progress");
33990
+ const completed = existsSync3(join4(entry.root, ".overai", "manifest.json"));
33991
+ if (entry.root !== root && existsSync3(entry.root)) {
33992
+ if (lstatSync(entry.root).isSymbolicLink())
33993
+ throw new Error(`Refusing to clone into symbolic link ${entry.root}.`);
33994
+ const savedId = completed ? readManifest(entry.root).workspaceId : existsSync3(marker) ? readFileSync6(marker, "utf8").trim() : null;
33995
+ if (partialCloneWorkspaceId !== workspaceId || savedId !== entry.workspace.id)
33996
+ throw new Error(`${entry.root} already exists. Choose another clone directory.`);
33997
+ }
33998
+ if (existsSync3(join4(entry.root, "workspaces")) && lstatSync(join4(entry.root, "workspaces")).isSymbolicLink())
33999
+ throw new Error(`Refusing to clone through symbolic link ${join4(entry.root, "workspaces")}.`);
34000
+ if (!completed) {
34001
+ const resourcesByAdapter = await Promise.all(allAdapters.map(async (adapter) => ({
34002
+ adapter,
34003
+ resources: await adapter.fetchAll(entry.workspace.id)
34004
+ })));
34005
+ plan.push({
34006
+ root: entry.root,
34007
+ description: entry.workspace.description,
34008
+ manifest: {
34009
+ workspaceId: entry.workspace.id,
34010
+ workspaceName: entry.workspace.name,
34011
+ organizationId: entry.workspace.organizationId,
34012
+ resources: {}
34013
+ },
34014
+ resourcesByAdapter
34015
+ });
34016
+ }
34017
+ let after = null;
34018
+ do {
34019
+ const data = await gqlRequest(
34020
+ `query CliCloneChildren($id: String!, $after: Cursor) {
34021
+ workspaceWorkspaces(condition: { parentWorkspaceId: $id }, first: 100, after: $after) {
34022
+ nodes { workspace { id name description organizationId isArchived } }
34023
+ pageInfo { hasNextPage endCursor }
34024
+ }
34025
+ }`,
34026
+ { id: entry.workspace.id, after }
34027
+ );
34028
+ for (const node of data.workspaceWorkspaces.nodes) {
34029
+ const child = node?.workspace;
34030
+ if (child == null || child.isArchived) continue;
34031
+ if (child.organizationId !== workspace.organizationId || seen.has(child.id))
34032
+ throw new Error(`Invalid workspace hierarchy at ${child.id}.`);
34033
+ if (!/^[a-zA-Z0-9_-]+$/.test(child.id))
34034
+ throw new Error(`Invalid workspace id ${child.id}.`);
34035
+ seen.add(child.id);
34036
+ pending.push({ workspace: child, root: join4(entry.root, "workspaces", `${slugify(child.name)}.${child.id}`) });
34037
+ }
34038
+ const page = data.workspaceWorkspaces.pageInfo;
34039
+ if (page.hasNextPage && (page.endCursor == null || page.endCursor === after))
34040
+ throw new Error("Workspace pagination did not advance.");
34041
+ after = page.hasNextPage ? page.endCursor : null;
34042
+ } while (after != null);
34043
+ }
34044
+ for (const entry of plan) {
34045
+ mkdirSync3(join4(entry.root, ".overai"), { recursive: true });
34046
+ writeFileSync4(join4(entry.root, ".overai", "clone-in-progress"), `${entry.manifest.workspaceId}
33993
34047
  `);
33994
- writeFileSync4(
33995
- join4(root, "WORKSPACE.md"),
33996
- workspace.description.length === 0 ? "" : `${workspace.description.trimEnd()}
34048
+ writeFileSync4(
34049
+ join4(entry.root, "WORKSPACE.md"),
34050
+ entry.description.length === 0 ? "" : `${entry.description.trimEnd()}
33997
34051
  `
33998
- );
33999
- const manifest = {
34000
- workspaceId: workspace.id,
34001
- workspaceName: workspace.name,
34002
- organizationId: workspace.organizationId,
34003
- resources: {}
34004
- };
34005
- for (const { adapter, resources: resources2 } of resourcesByAdapter) {
34006
- for (const remote of resources2) {
34007
- for (const [relPath, content] of Object.entries(remote.files)) {
34008
- writeSynced(root, relPath, content);
34052
+ );
34053
+ for (const { adapter, resources: resources2 } of entry.resourcesByAdapter) {
34054
+ for (const remote of resources2) {
34055
+ for (const [relPath, content] of Object.entries(remote.files)) {
34056
+ writeSynced(entry.root, relPath, content);
34057
+ }
34058
+ entry.manifest.resources[remote.id] = toManifestResource(adapter.type, remote);
34009
34059
  }
34010
- manifest.resources[remote.id] = toManifestResource(adapter.type, remote);
34060
+ process.stdout.write(`${entry.manifest.workspaceName}: ${adapter.type}: ${String(resources2.length)} cloned
34061
+ `);
34011
34062
  }
34012
- process.stdout.write(
34013
- `${adapter.type}: ${String(resources2.length)} cloned
34014
- `
34015
- );
34016
34063
  }
34017
- writeManifest(root, manifest);
34018
- unlinkSync(cloneMarker);
34064
+ for (const entry of plan.toReversed()) {
34065
+ writeManifest(entry.root, entry.manifest);
34066
+ unlinkSync(join4(entry.root, ".overai", "clone-in-progress"));
34067
+ }
34019
34068
  process.stdout.write(`
34020
- Cloned "${workspace.name}" into ${root}
34069
+ Cloned "${workspace.name}" and ${String(pending.length - 1)} descendant workspaces into ${root}
34021
34070
  `);
34022
34071
  };
34023
34072
  var collectStatus = async (root, manifest) => {
@@ -35262,9 +35311,12 @@ executionMemory MB).
35262
35311
  var workflow = `# CLI editing workflow
35263
35312
 
35264
35313
  1. \`overai workspace list\` to find the id, then \`overai clone <workspaceId>\`
35265
- (or \`overai pull\` in an existing clone). Workspaces nest, and clone pulls
35266
- ONE workspace \u2014 a parent's clone does not include its children, so clone
35267
- each nested workspace you need separately.
35314
+ (or \`overai pull\` in an existing clone). Clone includes accessible,
35315
+ non-archived descendants under \`workspaces/<slug>.<workspaceId>/\`, each with
35316
+ its own manifest and WORKSPACE.md. Change into a child folder to work there.
35317
+ All sync commands stay scoped to the nearest workspace, never its children.
35318
+ Pull does not discover new children; clone those separately. Moving local
35319
+ files does not move resources between workspaces in OverAI.
35268
35320
  2. Edit files. The id in each filename is authoritative; slugs are cosmetic.
35269
35321
  3. \`overai status\` / \`overai diff\` \u2014 review what changed.
35270
35322
  4. \`overai review\` \u2014 local checks, authenticated TypeScript lint against the
@@ -36230,7 +36282,7 @@ Usage:
36230
36282
  overai sql credential rotate|revoke <credentialId>
36231
36283
 
36232
36284
  Workspace sync (edit resources as local files):
36233
- overai clone <workspaceId> [dir] Materialize resources and WORKSPACE.md
36285
+ overai clone <workspaceId> [dir] Clone workspace and accessible descendants
36234
36286
  overai status Local edits + remote drift
36235
36287
  overai diff [filter] [--from <ref>] [--to <ref>]
36236
36288
  Diff versions (refs: N, base, local, deployed, latest;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overaihq/cli",
3
- "version": "0.10.3",
3
+ "version": "0.11.1",
4
4
  "description": "Create, edit, deploy, and run OverAI workspace resources from the command line",
5
5
  "type": "module",
6
6
  "homepage": "https://overai.com",