@corvio/cli 0.1.0-beta.26 → 0.1.0-beta.27
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 +3 -3
- package/dist/cli.js +81 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,8 +15,8 @@ corvio files get <asset_id> --content --json
|
|
|
15
15
|
corvio files organize <asset_id> --instruction 'Create a reusable decision brief' --yes --json
|
|
16
16
|
corvio files operation <operation_id> --json
|
|
17
17
|
corvio files resume <operation_id> --yes --json
|
|
18
|
-
corvio docs table-read <document_id> --limit 25 --json
|
|
19
|
-
corvio docs table-mutate <document_id> --input ./table-change.json --change-summary 'Updated owner and due date' --yes --json
|
|
18
|
+
corvio docs table-read <workspace_id/document_id> --limit 25 --json
|
|
19
|
+
corvio docs table-mutate <workspace_id/document_id> --input ./table-change.json --change-summary 'Updated owner and due date' --yes --json
|
|
20
20
|
corvio sync init --dir ./knowledge --root-node-id <node_id> --json
|
|
21
21
|
corvio update check --json
|
|
22
22
|
```
|
|
@@ -32,7 +32,7 @@ fresh host session. Until a reviewed WorkBuddy marketplace listing is live, a Wo
|
|
|
32
32
|
auto-update from Corvio. Remote MCP changes are server-delivered and normally need only a refreshed host session unless new OAuth scopes
|
|
33
33
|
require reauthorization.
|
|
34
34
|
|
|
35
|
-
The CLI rejects unknown/duplicate options and malformed integer bounds before network access. Normal API calls have a bounded timeout (`CORVIO_REQUEST_TIMEOUT_MS`, maximum ten minutes); only reads, explicit idempotency contracts, revision guards, and other owner-declared safe operations retry automatically. Document updates preflight the current revision when `--expected-revision` is omitted. `docs table-read` returns a bounded stable-ID projection; `docs table-mutate` accepts a JSON payload for at most 50 cell updates or row appends and requires the fetched revision, while formulas, styles, structure, sorting, and semantic transformations remain `corvio ask --allow-actions` work. When the executing principal is a connected Agent, document and table mutations also require `--change-summary`, create a visible document-level comment after the guarded update, and return its receipt; a comment failure is reported as a partial effect rather than silent success. When a Corvio document or comment supplied the task, use `corvio agent closeout` to return the verified outcome, optionally resolve the thread, and read both document and thread back. Downloads, uploads, and Markdown pulls verify SHA-256 receipts before replacing local files; local writes are atomic, remote default filenames cannot escape the current directory, and Sync refuses symbolic-link traversal while checkpointing each successful push.
|
|
35
|
+
The CLI rejects unknown/duplicate options and malformed integer bounds before network access. Normal API calls have a bounded timeout (`CORVIO_REQUEST_TIMEOUT_MS`, maximum ten minutes); only reads, explicit idempotency contracts, revision guards, and other owner-declared safe operations retry automatically. Document commands accept both bare document IDs and canonical `workspace_id/document_id` handles; a canonical handle that disagrees with the selected Workspace fails before remote access, and successful reads return `canonical_id` for direct reuse. Document updates preflight the current revision when `--expected-revision` is omitted. `docs table-read` returns a bounded stable-ID projection; `docs table-mutate` accepts a JSON payload for at most 50 cell updates or row appends and requires the fetched revision, while formulas, styles, structure, sorting, and semantic transformations remain `corvio ask --allow-actions` work. When the executing principal is a connected Agent, document and table mutations also require `--change-summary`, create a visible document-level comment after the guarded update, and return its receipt; a comment failure is reported as a partial effect rather than silent success. When a Corvio document or comment supplied the task, use `corvio agent closeout` to return the verified outcome, optionally resolve the thread, and read both document and thread back. Downloads, uploads, and Markdown pulls verify SHA-256 receipts before replacing local files; local writes are atomic, remote default filenames cannot escape the current directory, and Sync refuses symbolic-link traversal while checkpointing each successful push.
|
|
36
36
|
|
|
37
37
|
For `corvio ask`, pass the user's natural goal, complete decision-relevant context, and explicit constraints. Unless the user chose them,
|
|
38
38
|
do not invent taxonomy, titles, artifact counts, or Corvio's internal plan. Read the terminal Question receipt and use `artifact.url` or
|
package/dist/cli.js
CHANGED
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
agentCredentialPath,
|
|
40
40
|
} from "./core.js";
|
|
41
41
|
|
|
42
|
-
const VERSION = "0.1.0-beta.
|
|
42
|
+
const VERSION = "0.1.0-beta.27";
|
|
43
43
|
const DEFAULT_API_BASE = "https://api.corvio.ai/v1";
|
|
44
44
|
const DEFAULT_APP_BASE = "https://corvio.ai";
|
|
45
45
|
const USER_AGENT = `corvio-cli/${VERSION}`;
|
|
@@ -189,8 +189,11 @@ const COMMAND_HELP = Object.freeze({
|
|
|
189
189
|
|
|
190
190
|
function contextualHelp(command, action) {
|
|
191
191
|
const normalizedCommand = command === "workspace" ? "workspaces" : command === "folders" ? "projects" : command;
|
|
192
|
-
const
|
|
193
|
-
if (!
|
|
192
|
+
const baseCommandHelp = COMMAND_HELP[normalizedCommand];
|
|
193
|
+
if (!baseCommandHelp) return HELP.trimEnd();
|
|
194
|
+
const commandHelp = normalizedCommand === "docs"
|
|
195
|
+
? `${baseCommandHelp}\n\nDocument commands accept either a bare document ID or the canonical workspace_id/document_id handle returned by Corvio. A canonical handle that disagrees with the selected Workspace fails before any remote request. Document reads return canonical_id for direct reuse.`
|
|
196
|
+
: baseCommandHelp;
|
|
194
197
|
if (!action) return commandHelp;
|
|
195
198
|
const matchingLine = commandHelp
|
|
196
199
|
.split("\n")
|
|
@@ -277,6 +280,53 @@ function resourcePath(value) {
|
|
|
277
280
|
return encodeURIComponent(String(value));
|
|
278
281
|
}
|
|
279
282
|
|
|
283
|
+
function resolveDocumentId(value, selection) {
|
|
284
|
+
const raw = String(value || "").trim();
|
|
285
|
+
if (!raw) {
|
|
286
|
+
throw new CliError("Provide a document ID or canonical workspace/document handle.", {
|
|
287
|
+
code: "document_id_required",
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const handle = raw.startsWith("document:") ? raw.slice("document:".length) : raw;
|
|
291
|
+
if (!handle.includes("/")) return handle;
|
|
292
|
+
const parts = handle.split("/");
|
|
293
|
+
if (parts.length !== 2 || parts.some((part) => !part.trim())) {
|
|
294
|
+
throw new CliError(
|
|
295
|
+
"Document handles must be a document ID or canonical workspace_id/document_id pair.",
|
|
296
|
+
{ code: "document_handle_invalid" },
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
const [workspaceId, documentId] = parts.map((part) => part.trim());
|
|
300
|
+
if (!selection?.workspaceId) {
|
|
301
|
+
throw new CliError(
|
|
302
|
+
"A canonical document handle requires an explicit or resolved Workspace route.",
|
|
303
|
+
{ code: "document_workspace_route_unavailable", exitCode: EXIT.AUTH },
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
if (selection?.workspaceId && workspaceId !== selection.workspaceId) {
|
|
307
|
+
throw new CliError(
|
|
308
|
+
`The document handle belongs to Workspace ${workspaceId}, but the current route is ${selection.workspaceId}.`,
|
|
309
|
+
{
|
|
310
|
+
code: "document_workspace_mismatch",
|
|
311
|
+
exitCode: EXIT.AUTH,
|
|
312
|
+
details: {
|
|
313
|
+
handle_workspace_id: workspaceId,
|
|
314
|
+
selected_workspace_id: selection.workspaceId,
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
return documentId;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function withCanonicalDocumentId(value, selection, fallbackDocumentId = null) {
|
|
323
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
324
|
+
const workspaceId = String(selection?.workspaceId || "").trim();
|
|
325
|
+
const documentId = String(fallbackDocumentId || value.id || "").trim();
|
|
326
|
+
if (!workspaceId || !documentId) return value;
|
|
327
|
+
return { ...value, canonical_id: `${workspaceId}/${documentId}` };
|
|
328
|
+
}
|
|
329
|
+
|
|
280
330
|
function safeRemoteFileName(value, fallback) {
|
|
281
331
|
const normalized = String(value || "").replaceAll("\\", "/");
|
|
282
332
|
const name = basename(normalized).trim();
|
|
@@ -2821,7 +2871,7 @@ async function search(options, positionals) {
|
|
|
2821
2871
|
}
|
|
2822
2872
|
|
|
2823
2873
|
async function docs(options, action, id) {
|
|
2824
|
-
const { api, executionIdentity } = await executionClient(options);
|
|
2874
|
+
const { api, executionIdentity, selection } = await executionClient(options);
|
|
2825
2875
|
const documentId = id || options.id;
|
|
2826
2876
|
let result;
|
|
2827
2877
|
if (action === "list") {
|
|
@@ -2830,8 +2880,19 @@ async function docs(options, action, id) {
|
|
|
2830
2880
|
throw new CliError("--lifecycle must be active or archived.", { code: "document_lifecycle_invalid" });
|
|
2831
2881
|
}
|
|
2832
2882
|
result = await api.request("/documents", { query: { ...pageQuery(options), lifecycle } });
|
|
2883
|
+
if (Array.isArray(result?.data)) {
|
|
2884
|
+
result = {
|
|
2885
|
+
...result,
|
|
2886
|
+
data: result.data.map((item) => withCanonicalDocumentId(item, selection)),
|
|
2887
|
+
};
|
|
2888
|
+
}
|
|
2833
2889
|
} else if (action === "get") {
|
|
2834
|
-
|
|
2890
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
2891
|
+
result = withCanonicalDocumentId(
|
|
2892
|
+
await api.request(`/documents/${resourcePath(targetId)}`),
|
|
2893
|
+
selection,
|
|
2894
|
+
targetId,
|
|
2895
|
+
);
|
|
2835
2896
|
if (executionIdentity === "agent") {
|
|
2836
2897
|
result = {
|
|
2837
2898
|
...result,
|
|
@@ -2843,7 +2904,7 @@ async function docs(options, action, id) {
|
|
|
2843
2904
|
result = { ...result, output: options.output };
|
|
2844
2905
|
}
|
|
2845
2906
|
} else if (action === "table-read") {
|
|
2846
|
-
const targetId = documentId
|
|
2907
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
2847
2908
|
result = await api.request(`/documents/${resourcePath(targetId)}/table`, {
|
|
2848
2909
|
query: {
|
|
2849
2910
|
table_id: options["table-id"] || undefined,
|
|
@@ -2853,7 +2914,7 @@ async function docs(options, action, id) {
|
|
|
2853
2914
|
});
|
|
2854
2915
|
} else if (action === "table-mutate") {
|
|
2855
2916
|
requireConfirmation(options, "Mutating a document table");
|
|
2856
|
-
const targetId = documentId
|
|
2917
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
2857
2918
|
const payload = await readJsonObject(option(options, "input", { required: true }), "--input");
|
|
2858
2919
|
const changeSummary = String(options["change-summary"] || "").trim();
|
|
2859
2920
|
if (executionIdentity === "agent" && !changeSummary) {
|
|
@@ -2931,7 +2992,7 @@ async function docs(options, action, id) {
|
|
|
2931
2992
|
code: "document_update_empty",
|
|
2932
2993
|
});
|
|
2933
2994
|
}
|
|
2934
|
-
const targetId = documentId
|
|
2995
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
2935
2996
|
const changeSummary = String(options["change-summary"] || "").trim();
|
|
2936
2997
|
if (executionIdentity === "agent" && !changeSummary) {
|
|
2937
2998
|
throw new CliError(
|
|
@@ -3005,7 +3066,8 @@ async function docs(options, action, id) {
|
|
|
3005
3066
|
}
|
|
3006
3067
|
} else if (action === "move") {
|
|
3007
3068
|
rejectTogether(options, "before-node-id", "after-node-id");
|
|
3008
|
-
const
|
|
3069
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3070
|
+
const current = await api.request(`/documents/${resourcePath(targetId)}`);
|
|
3009
3071
|
result = await api.request(`/nodes/${resourcePath(current.node_id)}:move`, {
|
|
3010
3072
|
method: "POST",
|
|
3011
3073
|
headers: { "idempotency-key": idempotencyKey(options) },
|
|
@@ -3017,25 +3079,31 @@ async function docs(options, action, id) {
|
|
|
3017
3079
|
});
|
|
3018
3080
|
} else if (action === "archive") {
|
|
3019
3081
|
requireConfirmation(options, "Archiving a document");
|
|
3020
|
-
|
|
3082
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3083
|
+
result = await api.request(`/documents/${resourcePath(targetId)}`, { method: "DELETE" });
|
|
3021
3084
|
} else if (action === "restore") {
|
|
3022
3085
|
requireConfirmation(options, "Restoring an archived document");
|
|
3023
|
-
|
|
3086
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3087
|
+
result = await api.request(`/documents/${resourcePath(targetId)}/restore`, { method: "POST", retrySafe: true });
|
|
3024
3088
|
} else if (action === "share") {
|
|
3025
3089
|
requireConfirmation(options, "Creating a document share link");
|
|
3026
|
-
|
|
3090
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3091
|
+
result = await api.request(`/documents/${resourcePath(targetId)}/shares`, {
|
|
3027
3092
|
method: "POST",
|
|
3028
3093
|
retrySafe: true,
|
|
3029
3094
|
body: { public_display_name: options["display-name"] || null },
|
|
3030
3095
|
});
|
|
3031
3096
|
} else if (action === "open") {
|
|
3032
|
-
const targetId = documentId
|
|
3097
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3033
3098
|
const target = `${String(process.env.CORVIO_APP_BASE || DEFAULT_APP_BASE).replace(/\/+$/, "")}/app/d/${resourcePath(targetId)}`;
|
|
3034
3099
|
const opened = options["no-input"] ? false : await openBrowser(target);
|
|
3035
3100
|
result = { document_id: targetId, url: target, opened };
|
|
3036
3101
|
} else {
|
|
3037
3102
|
throw new CliError("docs action must be list, get, table-read, table-mutate, create, update, move, archive, restore, share, or open.");
|
|
3038
3103
|
}
|
|
3104
|
+
if (!new Set(["list", "create"]).has(action)) {
|
|
3105
|
+
result = withCanonicalDocumentId(result, selection, resolveDocumentId(documentId, selection));
|
|
3106
|
+
}
|
|
3039
3107
|
writeResult(result, { json: options.json });
|
|
3040
3108
|
}
|
|
3041
3109
|
|