@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.
- package/README.md +12 -1
- package/dist/index.js +87 -35
- 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> #
|
|
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
|
|
33985
|
-
|
|
33986
|
-
|
|
33987
|
-
|
|
33988
|
-
|
|
33989
|
-
|
|
33990
|
-
|
|
33991
|
-
|
|
33992
|
-
|
|
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
|
-
|
|
33995
|
-
|
|
33996
|
-
|
|
34048
|
+
writeFileSync4(
|
|
34049
|
+
join4(entry.root, "WORKSPACE.md"),
|
|
34050
|
+
entry.description.length === 0 ? "" : `${entry.description.trimEnd()}
|
|
33997
34051
|
`
|
|
33998
|
-
|
|
33999
|
-
|
|
34000
|
-
|
|
34001
|
-
|
|
34002
|
-
|
|
34003
|
-
|
|
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.
|
|
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
|
-
|
|
34018
|
-
|
|
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).
|
|
35266
|
-
|
|
35267
|
-
|
|
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]
|
|
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;
|