@corvio/cli 0.1.0-beta.26 → 0.1.0-beta.28
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 +4 -4
- package/dist/cli.js +90 -15
- package/dist/core.js +19 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,13 +15,13 @@ 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
|
```
|
|
23
23
|
|
|
24
|
-
When no environment key is configured, run `corvio auth login`. Login creates one user-level `cvu_` control-plane credential; it does not freeze the browser's current Workspace. Run `corvio agents connect --project .` from the project you want to expose. The command verifies the selected Codex, Claude Code, or Copilot host before server mutation, then idempotently creates or restores the Agent for that provider/device/project binding, grants current/future Workspace coverage under live membership/role and ACL limits, saves the one-time `cvg_` in a `0600` multi-binding registry, and starts that project's listener. `corvio collaboration status` then checks local foreground prerequisites and reports package freshness, contract compatibility, installation ownership, every discoverable copy, and the fact that host loading is not observable. A listener receives inbound `@Agent` work; it does not make an unrelated foreground coding session collaborate automatically. Another project gets another Agent. Ordinary data commands prefer the binding whose root contains the current directory, then a locally confirmed selection, the account selection, the personal default, or one authorized candidate. They ask only on real ambiguity. Use `corvio workspaces use`, `--workspace`, and `--agent` as explicit routing/reproducibility controls. Legacy Workspace-bound `cvk_`, `cvg_`, and version-1 credential files remain compatible and are never widened. Local sync stores only credential-free handles and revision/hash baselines in `.corvio/state.json`; Markdown lives below `corvio_docs/`, and conflicts require explicit resolution.
|
|
24
|
+
When no environment key is configured, run `corvio auth login`. Login creates one user-level `cvu_` control-plane credential; it does not freeze the browser's current Workspace. Run `corvio agents connect --project .` from the project you want to expose. The command verifies the selected Codex, Claude Code, or Copilot host before server mutation, then idempotently creates or restores the Agent for that provider/device/project binding, grants current/future Workspace coverage under live membership/role and ACL limits, saves the one-time `cvg_` in a `0600` multi-binding registry, and starts that project's listener. `corvio collaboration status` then checks local foreground prerequisites and reports package freshness, contract compatibility, installation ownership, every discoverable copy, and the fact that host loading is not observable. A listener receives inbound `@Agent` work; it does not make an unrelated foreground coding session collaborate automatically. Another project gets another Agent. Ordinary data commands prefer the binding whose root contains the current directory, then a locally confirmed selection, the account selection, the personal default, or one authorized candidate. They ask only on real ambiguity. Use `corvio workspaces use`, `--workspace`, and `--agent` as explicit routing/reproducibility controls. Legacy Workspace-bound `cvk_`, `cvg_`, and version-1 credential files remain compatible and are never widened. Local sync stores only credential-free handles and revision/hash baselines in `.corvio/state.json`; Markdown lives below `corvio_docs/`, and conflicts require explicit resolution. `corvio capabilities` reads only Corvio API capability and compatibility facts, so discovery remains usable in hosts that deny npm registry access. `corvio update check` is the explicit registry-backed version check; it never self-installs. See [the Corvio CLI guide](https://corvio.ai/developers/cli) for the full command and receipt contract.
|
|
25
25
|
|
|
26
26
|
CLI and Skill updates are independent. `corvio update check` checks the CLI package only. `corvio collaboration status` detects a missing,
|
|
27
27
|
incompatible, compatible-but-not-current, or conflicting local Skill, but never mutates host directories. Use
|
|
@@ -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.28";
|
|
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();
|
|
@@ -699,7 +749,10 @@ async function capabilities(options) {
|
|
|
699
749
|
workspace_selection: selection,
|
|
700
750
|
api: index,
|
|
701
751
|
principal: identity,
|
|
702
|
-
update: await buildUpdateStatus(index, {
|
|
752
|
+
update: await buildUpdateStatus(index, {
|
|
753
|
+
allowRegistryFailure: true,
|
|
754
|
+
checkRegistry: false,
|
|
755
|
+
}),
|
|
703
756
|
}, { json: options.json });
|
|
704
757
|
}
|
|
705
758
|
|
|
@@ -2821,7 +2874,7 @@ async function search(options, positionals) {
|
|
|
2821
2874
|
}
|
|
2822
2875
|
|
|
2823
2876
|
async function docs(options, action, id) {
|
|
2824
|
-
const { api, executionIdentity } = await executionClient(options);
|
|
2877
|
+
const { api, executionIdentity, selection } = await executionClient(options);
|
|
2825
2878
|
const documentId = id || options.id;
|
|
2826
2879
|
let result;
|
|
2827
2880
|
if (action === "list") {
|
|
@@ -2830,8 +2883,19 @@ async function docs(options, action, id) {
|
|
|
2830
2883
|
throw new CliError("--lifecycle must be active or archived.", { code: "document_lifecycle_invalid" });
|
|
2831
2884
|
}
|
|
2832
2885
|
result = await api.request("/documents", { query: { ...pageQuery(options), lifecycle } });
|
|
2886
|
+
if (Array.isArray(result?.data)) {
|
|
2887
|
+
result = {
|
|
2888
|
+
...result,
|
|
2889
|
+
data: result.data.map((item) => withCanonicalDocumentId(item, selection)),
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2833
2892
|
} else if (action === "get") {
|
|
2834
|
-
|
|
2893
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
2894
|
+
result = withCanonicalDocumentId(
|
|
2895
|
+
await api.request(`/documents/${resourcePath(targetId)}`),
|
|
2896
|
+
selection,
|
|
2897
|
+
targetId,
|
|
2898
|
+
);
|
|
2835
2899
|
if (executionIdentity === "agent") {
|
|
2836
2900
|
result = {
|
|
2837
2901
|
...result,
|
|
@@ -2843,7 +2907,7 @@ async function docs(options, action, id) {
|
|
|
2843
2907
|
result = { ...result, output: options.output };
|
|
2844
2908
|
}
|
|
2845
2909
|
} else if (action === "table-read") {
|
|
2846
|
-
const targetId = documentId
|
|
2910
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
2847
2911
|
result = await api.request(`/documents/${resourcePath(targetId)}/table`, {
|
|
2848
2912
|
query: {
|
|
2849
2913
|
table_id: options["table-id"] || undefined,
|
|
@@ -2853,7 +2917,7 @@ async function docs(options, action, id) {
|
|
|
2853
2917
|
});
|
|
2854
2918
|
} else if (action === "table-mutate") {
|
|
2855
2919
|
requireConfirmation(options, "Mutating a document table");
|
|
2856
|
-
const targetId = documentId
|
|
2920
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
2857
2921
|
const payload = await readJsonObject(option(options, "input", { required: true }), "--input");
|
|
2858
2922
|
const changeSummary = String(options["change-summary"] || "").trim();
|
|
2859
2923
|
if (executionIdentity === "agent" && !changeSummary) {
|
|
@@ -2931,7 +2995,7 @@ async function docs(options, action, id) {
|
|
|
2931
2995
|
code: "document_update_empty",
|
|
2932
2996
|
});
|
|
2933
2997
|
}
|
|
2934
|
-
const targetId = documentId
|
|
2998
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
2935
2999
|
const changeSummary = String(options["change-summary"] || "").trim();
|
|
2936
3000
|
if (executionIdentity === "agent" && !changeSummary) {
|
|
2937
3001
|
throw new CliError(
|
|
@@ -3005,7 +3069,8 @@ async function docs(options, action, id) {
|
|
|
3005
3069
|
}
|
|
3006
3070
|
} else if (action === "move") {
|
|
3007
3071
|
rejectTogether(options, "before-node-id", "after-node-id");
|
|
3008
|
-
const
|
|
3072
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3073
|
+
const current = await api.request(`/documents/${resourcePath(targetId)}`);
|
|
3009
3074
|
result = await api.request(`/nodes/${resourcePath(current.node_id)}:move`, {
|
|
3010
3075
|
method: "POST",
|
|
3011
3076
|
headers: { "idempotency-key": idempotencyKey(options) },
|
|
@@ -3017,25 +3082,31 @@ async function docs(options, action, id) {
|
|
|
3017
3082
|
});
|
|
3018
3083
|
} else if (action === "archive") {
|
|
3019
3084
|
requireConfirmation(options, "Archiving a document");
|
|
3020
|
-
|
|
3085
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3086
|
+
result = await api.request(`/documents/${resourcePath(targetId)}`, { method: "DELETE" });
|
|
3021
3087
|
} else if (action === "restore") {
|
|
3022
3088
|
requireConfirmation(options, "Restoring an archived document");
|
|
3023
|
-
|
|
3089
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3090
|
+
result = await api.request(`/documents/${resourcePath(targetId)}/restore`, { method: "POST", retrySafe: true });
|
|
3024
3091
|
} else if (action === "share") {
|
|
3025
3092
|
requireConfirmation(options, "Creating a document share link");
|
|
3026
|
-
|
|
3093
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3094
|
+
result = await api.request(`/documents/${resourcePath(targetId)}/shares`, {
|
|
3027
3095
|
method: "POST",
|
|
3028
3096
|
retrySafe: true,
|
|
3029
3097
|
body: { public_display_name: options["display-name"] || null },
|
|
3030
3098
|
});
|
|
3031
3099
|
} else if (action === "open") {
|
|
3032
|
-
const targetId = documentId
|
|
3100
|
+
const targetId = resolveDocumentId(documentId, selection);
|
|
3033
3101
|
const target = `${String(process.env.CORVIO_APP_BASE || DEFAULT_APP_BASE).replace(/\/+$/, "")}/app/d/${resourcePath(targetId)}`;
|
|
3034
3102
|
const opened = options["no-input"] ? false : await openBrowser(target);
|
|
3035
3103
|
result = { document_id: targetId, url: target, opened };
|
|
3036
3104
|
} else {
|
|
3037
3105
|
throw new CliError("docs action must be list, get, table-read, table-mutate, create, update, move, archive, restore, share, or open.");
|
|
3038
3106
|
}
|
|
3107
|
+
if (!new Set(["list", "create"]).has(action)) {
|
|
3108
|
+
result = withCanonicalDocumentId(result, selection, resolveDocumentId(documentId, selection));
|
|
3109
|
+
}
|
|
3039
3110
|
writeResult(result, { json: options.json });
|
|
3040
3111
|
}
|
|
3041
3112
|
|
|
@@ -3056,7 +3127,10 @@ async function projects(options, action) {
|
|
|
3056
3127
|
writeResult(result, { json: options.json });
|
|
3057
3128
|
}
|
|
3058
3129
|
|
|
3059
|
-
async function buildUpdateStatus(
|
|
3130
|
+
async function buildUpdateStatus(
|
|
3131
|
+
index,
|
|
3132
|
+
{ allowRegistryFailure = false, checkRegistry = true } = {},
|
|
3133
|
+
) {
|
|
3060
3134
|
return resolveNpmUpdateStatus({
|
|
3061
3135
|
currentVersion: VERSION,
|
|
3062
3136
|
policy: index?.distribution?.workspace_cli || {
|
|
@@ -3065,6 +3139,7 @@ async function buildUpdateStatus(index, { allowRegistryFailure = false } = {}) {
|
|
|
3065
3139
|
},
|
|
3066
3140
|
userAgent: USER_AGENT,
|
|
3067
3141
|
allowRegistryFailure,
|
|
3142
|
+
checkRegistry,
|
|
3068
3143
|
});
|
|
3069
3144
|
}
|
|
3070
3145
|
|
package/dist/core.js
CHANGED
|
@@ -244,6 +244,7 @@ export async function resolveNpmUpdateStatus({
|
|
|
244
244
|
policy,
|
|
245
245
|
userAgent,
|
|
246
246
|
allowRegistryFailure = false,
|
|
247
|
+
checkRegistry = true,
|
|
247
248
|
fetchImpl = globalThis.fetch,
|
|
248
249
|
registryBase = process.env.CORVIO_NPM_REGISTRY_BASE || "https://registry.npmjs.org",
|
|
249
250
|
}) {
|
|
@@ -253,22 +254,24 @@ export async function resolveNpmUpdateStatus({
|
|
|
253
254
|
const encoded = encodeURIComponent(packageName).replace("%40", "@");
|
|
254
255
|
let latestVersion = null;
|
|
255
256
|
let registryError = null;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (!response.ok) throw new Error(`npm registry returned ${response.status}`);
|
|
263
|
-
const metadata = await response.json();
|
|
264
|
-
latestVersion = String(metadata?.["dist-tags"]?.[distTag] || "").trim() || null;
|
|
265
|
-
} catch (error) {
|
|
266
|
-
registryError = error instanceof Error ? error.message : String(error);
|
|
267
|
-
if (!allowRegistryFailure) {
|
|
268
|
-
throw new CliError(`Unable to check npm for updates: ${registryError}`, {
|
|
269
|
-
code: "update_check_failed",
|
|
270
|
-
exitCode: EXIT.REMOTE,
|
|
257
|
+
if (checkRegistry) {
|
|
258
|
+
try {
|
|
259
|
+
const registry = String(registryBase).replace(/\/+$/, "");
|
|
260
|
+
const response = await fetchImpl(`${registry}/${encoded}`, {
|
|
261
|
+
headers: { accept: "application/json", "user-agent": userAgent },
|
|
262
|
+
signal: AbortSignal.timeout(2500),
|
|
271
263
|
});
|
|
264
|
+
if (!response.ok) throw new Error(`npm registry returned ${response.status}`);
|
|
265
|
+
const metadata = await response.json();
|
|
266
|
+
latestVersion = String(metadata?.["dist-tags"]?.[distTag] || "").trim() || null;
|
|
267
|
+
} catch (error) {
|
|
268
|
+
registryError = error instanceof Error ? error.message : String(error);
|
|
269
|
+
if (!allowRegistryFailure) {
|
|
270
|
+
throw new CliError(`Unable to check npm for updates: ${registryError}`, {
|
|
271
|
+
code: "update_check_failed",
|
|
272
|
+
exitCode: EXIT.REMOTE,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
272
275
|
}
|
|
273
276
|
}
|
|
274
277
|
const minimum = String(policy.minimum_supported_version || "").trim() || null;
|
|
@@ -278,6 +281,7 @@ export async function resolveNpmUpdateStatus({
|
|
|
278
281
|
dist_tag: distTag,
|
|
279
282
|
current_version: currentVersion,
|
|
280
283
|
latest_published_version: latestVersion,
|
|
284
|
+
registry_checked: checkRegistry,
|
|
281
285
|
reviewed_version: String(policy.reviewed_version || "").trim() || null,
|
|
282
286
|
minimum_supported_version: minimum,
|
|
283
287
|
update_available: Boolean(latestVersion && compareVersions(latestVersion, currentVersion) > 0),
|