@paneui/cli 0.0.6 → 0.0.8
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 +9 -9
- package/dist/commands/agent.js +17 -4
- package/dist/commands/attachment-delete.js +37 -0
- package/dist/commands/{blob-download.js → attachment-download.js} +15 -11
- package/dist/commands/{blob-list.js → attachment-list.js} +9 -9
- package/dist/commands/{blob-show.js → attachment-show.js} +11 -11
- package/dist/commands/{blob-token.js → attachment-token.js} +31 -31
- package/dist/commands/{blob-upload.js → attachment-upload.js} +20 -20
- package/dist/commands/{blob.js → attachment.js} +40 -40
- package/dist/commands/claim.js +68 -0
- package/dist/commands/create.js +71 -71
- package/dist/commands/delete.js +12 -12
- package/dist/commands/feedback.js +5 -5
- package/dist/commands/list.js +23 -23
- package/dist/commands/participant.js +38 -38
- package/dist/commands/send.js +33 -27
- package/dist/commands/state.js +12 -12
- package/dist/commands/{session.js → surface.js} +24 -24
- package/dist/commands/taste.js +14 -14
- package/dist/commands/{artifact.js → template.js} +76 -76
- package/dist/commands/watch.js +22 -22
- package/dist/index.js +17 -17
- package/dist/input.js +2 -2
- package/dist/output.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/commands/blob-delete.js +0 -37
package/README.md
CHANGED
|
@@ -34,12 +34,12 @@ Uniform `pane <noun> <verb> [options]`:
|
|
|
34
34
|
```
|
|
35
35
|
pane agent register Provision an agent API key and save it locally
|
|
36
36
|
pane agent logout Clear the locally-saved URL + API key
|
|
37
|
-
pane
|
|
38
|
-
pane
|
|
39
|
-
pane
|
|
40
|
-
pane
|
|
41
|
-
pane
|
|
42
|
-
pane
|
|
37
|
+
pane surface create Create a surface — returns surface_id, urls, tokens
|
|
38
|
+
pane surface show <id> Non-blocking snapshot: metadata + event log
|
|
39
|
+
pane surface send <id> Emit an agent event into a surface
|
|
40
|
+
pane surface watch <id> Stream a surface's events as JSON-lines on stdout
|
|
41
|
+
pane surface delete <id> Close / delete a surface
|
|
42
|
+
pane template <verb> Manage reusable, versioned templates
|
|
43
43
|
pane key list | revoke Inspect or revoke your agent's API key
|
|
44
44
|
pane taste get | set | clear Read / write / clear UI-taste notes
|
|
45
45
|
pane feedback create | list Submit / list one-shot feedback to the operator
|
|
@@ -56,12 +56,12 @@ stdout is machine-readable JSON. Errors go to stderr as
|
|
|
56
56
|
`{"error":{"code","message"}}` with a non-zero exit.
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
|
-
SESSION=$(pane
|
|
60
|
-
pane
|
|
59
|
+
SESSION=$(pane surface create --template form --schema ./q.json | jq -r .surface_id)
|
|
60
|
+
pane surface watch "$SESSION" | jq 'select(.type == "human_response")'
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
## Links
|
|
64
64
|
|
|
65
65
|
- Repo: <https://github.com/aerolalit/paneui>
|
|
66
|
-
- Spec: <https://github.com/aerolalit/paneui/
|
|
66
|
+
- Spec: <https://github.com/aerolalit/paneui/attachment/main/docs/SPEC.md>
|
|
67
67
|
- License: MIT
|
package/dist/commands/agent.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// logout.ts.
|
|
10
10
|
import { runRegister } from "./register.js";
|
|
11
11
|
import { runLogout } from "./logout.js";
|
|
12
|
+
import { runClaim } from "./claim.js";
|
|
12
13
|
import { fail } from "../output.js";
|
|
13
14
|
export const agentHelp = `pane agent — manage this agent's identity on the relay
|
|
14
15
|
|
|
@@ -18,24 +19,36 @@ Usage:
|
|
|
18
19
|
Verbs:
|
|
19
20
|
register Provision an agent API key (POST /v1/register) and save it
|
|
20
21
|
to the CLI config file. Run this once before other commands.
|
|
22
|
+
claim <code> Bind this agent to a human via a one-shot claim code the
|
|
23
|
+
human generated in their Settings UI (POST /v1/agents/claim).
|
|
24
|
+
One-way; no unclaim in v1.
|
|
21
25
|
logout Clear the locally-saved relay URL + API key. Does NOT
|
|
22
26
|
revoke the key on the relay — use 'pane key revoke' for
|
|
23
27
|
that.
|
|
24
28
|
|
|
25
29
|
Run \`pane agent <verb> --help\` for verb-specific options.`;
|
|
26
30
|
export async function runAgent(args) {
|
|
31
|
+
// Strip the first positional (the verb) so each verb runner sees its
|
|
32
|
+
// own arguments at positionals[0..n].
|
|
33
|
+
const verbArgs = {
|
|
34
|
+
...args,
|
|
35
|
+
positionals: args.positionals.slice(1),
|
|
36
|
+
};
|
|
27
37
|
const verb = args.positionals[0];
|
|
28
38
|
switch (verb) {
|
|
29
39
|
case "register":
|
|
30
|
-
await runRegister(
|
|
40
|
+
await runRegister(verbArgs);
|
|
41
|
+
break;
|
|
42
|
+
case "claim":
|
|
43
|
+
await runClaim(verbArgs);
|
|
31
44
|
break;
|
|
32
45
|
case "logout":
|
|
33
|
-
await runLogout(
|
|
46
|
+
await runLogout(verbArgs);
|
|
34
47
|
break;
|
|
35
48
|
case undefined:
|
|
36
|
-
fail("missing verb — usage: pane agent <register|logout> (run 'pane agent --help')", "invalid_args");
|
|
49
|
+
fail("missing verb — usage: pane agent <register|claim|logout> (run 'pane agent --help')", "invalid_args");
|
|
37
50
|
break;
|
|
38
51
|
default:
|
|
39
|
-
fail(`unknown agent verb '${verb}' — expected register|logout (run 'pane agent --help')`, "invalid_args");
|
|
52
|
+
fail(`unknown agent verb '${verb}' — expected register|claim|logout (run 'pane agent --help')`, "invalid_args");
|
|
40
53
|
}
|
|
41
54
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// `pane attachment delete <attachment-id>` — soft-delete a attachment.
|
|
2
|
+
import { assertKnownFlags } from "../argv.js";
|
|
3
|
+
import { makeClient } from "../config.js";
|
|
4
|
+
import { fail, failFromError, printJson } from "../output.js";
|
|
5
|
+
const KNOWN_FLAGS = [];
|
|
6
|
+
const KNOWN_BOOLS = [];
|
|
7
|
+
export const blobDeleteHelp = `pane attachment delete — soft-delete a attachment
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
pane attachment delete <attachment-id> [options]
|
|
11
|
+
|
|
12
|
+
Marks the attachment as deleted (DELETE /v1/attachments/:id). Idempotent: deleting an
|
|
13
|
+
already-deleted attachment still returns success. Tokens minted against this attachment
|
|
14
|
+
become unusable.
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
--url <url> Relay base URL (overrides PANE_URL).
|
|
18
|
+
--api-key <key> Agent API key (overrides PANE_API_KEY).
|
|
19
|
+
-h, --help Show this help.
|
|
20
|
+
|
|
21
|
+
Output (stdout, JSON):
|
|
22
|
+
{ attachment_id, deleted: true }`;
|
|
23
|
+
export async function runBlobDelete(args) {
|
|
24
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane attachment delete");
|
|
25
|
+
const attachmentId = args.positionals[0];
|
|
26
|
+
if (!attachmentId) {
|
|
27
|
+
fail("missing <attachment-id> — 'pane attachment delete <attachment-id>'", "invalid_args");
|
|
28
|
+
}
|
|
29
|
+
const client = makeClient(args);
|
|
30
|
+
try {
|
|
31
|
+
const r = await client.deleteBlob(attachmentId);
|
|
32
|
+
printJson({ attachment_id: attachmentId, ...r });
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
failFromError(e);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane attachment download <attachment-id>` — fetch attachment bytes by id.
|
|
2
2
|
import { writeFileSync } from "node:fs";
|
|
3
3
|
import { assertKnownFlags } from "../argv.js";
|
|
4
4
|
import { makeClient } from "../config.js";
|
|
5
5
|
import { fail, failFromError, printJson } from "../output.js";
|
|
6
6
|
const KNOWN_FLAGS = ["out"];
|
|
7
7
|
const KNOWN_BOOLS = [];
|
|
8
|
-
export const blobDownloadHelp = `pane
|
|
8
|
+
export const blobDownloadHelp = `pane attachment download — fetch a attachment's bytes
|
|
9
9
|
|
|
10
10
|
Usage:
|
|
11
|
-
pane
|
|
11
|
+
pane attachment download <attachment-id> [--out <path>] [options]
|
|
12
12
|
|
|
13
|
-
GETs the
|
|
13
|
+
GETs the attachment bytes. With --out <path> the bytes are written to that file and
|
|
14
14
|
a JSON summary is printed on stdout; without --out the bytes are written to
|
|
15
15
|
stdout verbatim (useful for piping into another tool — but binary on a TTY
|
|
16
16
|
is rarely useful).
|
|
@@ -23,20 +23,24 @@ Options:
|
|
|
23
23
|
|
|
24
24
|
Output:
|
|
25
25
|
Without --out: raw bytes to stdout.
|
|
26
|
-
With --out: {
|
|
26
|
+
With --out: { attachment_id, written: <path>, bytes: <n> } to stdout.`;
|
|
27
27
|
export async function runBlobDownload(args) {
|
|
28
|
-
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane
|
|
29
|
-
const
|
|
30
|
-
if (!
|
|
31
|
-
fail("missing <
|
|
28
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane attachment download");
|
|
29
|
+
const attachmentId = args.positionals[0];
|
|
30
|
+
if (!attachmentId) {
|
|
31
|
+
fail("missing <attachment-id> — 'pane attachment download <attachment-id>'", "invalid_args");
|
|
32
32
|
}
|
|
33
33
|
const out = args.flags.get("out");
|
|
34
34
|
const client = makeClient(args);
|
|
35
35
|
try {
|
|
36
|
-
const buf = await client.downloadBlob(
|
|
36
|
+
const buf = await client.downloadBlob(attachmentId);
|
|
37
37
|
if (out) {
|
|
38
38
|
writeFileSync(out, Buffer.from(buf));
|
|
39
|
-
printJson({
|
|
39
|
+
printJson({
|
|
40
|
+
attachment_id: attachmentId,
|
|
41
|
+
written: out,
|
|
42
|
+
bytes: buf.byteLength,
|
|
43
|
+
});
|
|
40
44
|
}
|
|
41
45
|
else {
|
|
42
46
|
// Binary to stdout — useful for piping into another tool.
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane attachment list` — enumerate YOUR agent's attachments.
|
|
2
2
|
//
|
|
3
|
-
// Lists
|
|
4
|
-
// are excluded; tokens are not enumerated here (use 'pane
|
|
5
|
-
// <
|
|
3
|
+
// Lists attachments owned by the calling agent, newest first. Soft-deleted attachments
|
|
4
|
+
// are excluded; tokens are not enumerated here (use 'pane attachment token list
|
|
5
|
+
// <attachment-id>' for that).
|
|
6
6
|
import { assertKnownFlags } from "../argv.js";
|
|
7
7
|
import { makeClient } from "../config.js";
|
|
8
8
|
import { fail, printJson, failFromError } from "../output.js";
|
|
9
9
|
const KNOWN_FLAGS = ["cursor", "limit"];
|
|
10
10
|
const KNOWN_BOOLS = [];
|
|
11
|
-
export const blobListHelp = `pane
|
|
11
|
+
export const blobListHelp = `pane attachment list — enumerate YOUR agent's attachments
|
|
12
12
|
|
|
13
13
|
Usage:
|
|
14
|
-
pane
|
|
14
|
+
pane attachment list [--cursor <token>] [--limit <n>] [options]
|
|
15
15
|
|
|
16
|
-
Returns the agent's non-deleted
|
|
16
|
+
Returns the agent's non-deleted attachments (newest first). Paginated via opaque
|
|
17
17
|
cursor: when next_cursor is non-null in the response, pass it back as
|
|
18
18
|
--cursor to get the next page.
|
|
19
19
|
|
|
@@ -26,9 +26,9 @@ Options:
|
|
|
26
26
|
-h, --help Show this help.
|
|
27
27
|
|
|
28
28
|
Output (stdout, JSON):
|
|
29
|
-
{ items:
|
|
29
|
+
{ items: AttachmentRef[], next_cursor: string | null }`;
|
|
30
30
|
export async function runBlobList(args) {
|
|
31
|
-
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane
|
|
31
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane attachment list");
|
|
32
32
|
const cursor = args.flags.get("cursor");
|
|
33
33
|
const limitRaw = args.flags.get("limit");
|
|
34
34
|
let limit;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane attachment show <attachment-id>` — print a attachment's metadata.
|
|
2
2
|
import { assertKnownFlags } from "../argv.js";
|
|
3
3
|
import { makeClient } from "../config.js";
|
|
4
4
|
import { fail, failFromError, printJson } from "../output.js";
|
|
5
5
|
const KNOWN_FLAGS = [];
|
|
6
6
|
const KNOWN_BOOLS = [];
|
|
7
|
-
export const blobShowHelp = `pane
|
|
7
|
+
export const blobShowHelp = `pane attachment show — print a attachment's metadata (no bytes)
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
|
-
pane
|
|
10
|
+
pane attachment show <attachment-id> [options]
|
|
11
11
|
|
|
12
|
-
Looks up the
|
|
13
|
-
mime, size, sha256, etc. Does NOT download the bytes; use 'pane
|
|
12
|
+
Looks up the attachment by id and prints its AttachmentRef metadata — owner, scope,
|
|
13
|
+
mime, size, sha256, etc. Does NOT download the bytes; use 'pane attachment
|
|
14
14
|
download' for that.
|
|
15
15
|
|
|
16
16
|
Options:
|
|
@@ -19,16 +19,16 @@ Options:
|
|
|
19
19
|
-h, --help Show this help.
|
|
20
20
|
|
|
21
21
|
Output (stdout, JSON):
|
|
22
|
-
|
|
22
|
+
AttachmentRef`;
|
|
23
23
|
export async function runBlobShow(args) {
|
|
24
|
-
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane
|
|
25
|
-
const
|
|
26
|
-
if (!
|
|
27
|
-
fail("missing <
|
|
24
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane attachment show");
|
|
25
|
+
const attachmentId = args.positionals[0];
|
|
26
|
+
if (!attachmentId) {
|
|
27
|
+
fail("missing <attachment-id> — 'pane attachment show <attachment-id>'", "invalid_args");
|
|
28
28
|
}
|
|
29
29
|
const client = makeClient(args);
|
|
30
30
|
try {
|
|
31
|
-
const ref = await client.getBlob(
|
|
31
|
+
const ref = await client.getBlob(attachmentId);
|
|
32
32
|
printJson(ref);
|
|
33
33
|
}
|
|
34
34
|
catch (e) {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane attachment token <mint|revoke|list>` — capability URLs for a attachment.
|
|
2
2
|
//
|
|
3
|
-
// A capability URL (/b/<token>) is a participant-facing way to fetch a
|
|
4
|
-
// without holding the agent's API key. Tokens are minted per-
|
|
3
|
+
// A capability URL (/b/<token>) is a participant-facing way to fetch a attachment
|
|
4
|
+
// without holding the agent's API key. Tokens are minted per-attachment, can be
|
|
5
5
|
// time-bound (--ttl) and/or single-use (--once), and are stored hashed on
|
|
6
6
|
// the relay — the plaintext token is returned ONCE on 'mint' and cannot be
|
|
7
7
|
// recovered.
|
|
8
8
|
//
|
|
9
|
-
// This file is a sub-noun dispatcher under `pane
|
|
9
|
+
// This file is a sub-noun dispatcher under `pane attachment`. The attachment dispatcher
|
|
10
10
|
// hands us a ParsedArgs whose positionals[0] is "token" (our sub-noun
|
|
11
11
|
// marker), so we read the verb from positionals[1] and the args from
|
|
12
12
|
// positionals[2..]. Mirrors how participant.ts dispatches under `pane
|
|
13
|
-
//
|
|
13
|
+
// surface participant`.
|
|
14
14
|
import { assertKnownFlags } from "../argv.js";
|
|
15
15
|
import { makeClient } from "../config.js";
|
|
16
16
|
import { fail, failFromError, printJson } from "../output.js";
|
|
@@ -18,31 +18,31 @@ const MINT_FLAGS = ["ttl"];
|
|
|
18
18
|
const MINT_BOOLS = ["once"];
|
|
19
19
|
const NO_FLAGS = [];
|
|
20
20
|
const NO_BOOLS = [];
|
|
21
|
-
export const blobTokenHelp = `pane
|
|
21
|
+
export const blobTokenHelp = `pane attachment token — manage a attachment's capability URLs
|
|
22
22
|
|
|
23
23
|
Capability URLs let a participant (or any browser holding the URL) fetch a
|
|
24
|
-
|
|
24
|
+
attachment without the agent's API key. Tokens are stored HASHED on the relay; the
|
|
25
25
|
plaintext token is returned only ONCE from 'mint' — save the response before
|
|
26
26
|
delivering the URL.
|
|
27
27
|
|
|
28
28
|
Usage:
|
|
29
|
-
pane
|
|
29
|
+
pane attachment token <verb> <args>
|
|
30
30
|
|
|
31
31
|
Verbs:
|
|
32
|
-
mint <
|
|
32
|
+
mint <attachment-id> Mint a /b/<token> capability URL for one attachment.
|
|
33
33
|
Optional: --ttl <seconds> (defaults by scope:
|
|
34
|
-
30d
|
|
34
|
+
30d template / surface TTL / 24h agent; the caller
|
|
35
35
|
can only shorten), --once (token self-deletes on
|
|
36
36
|
first successful GET). Returns { token, url,
|
|
37
37
|
expires_at, ... } — ONCE.
|
|
38
38
|
|
|
39
|
-
revoke <
|
|
39
|
+
revoke <attachment-id> <token-id>
|
|
40
40
|
Invalidate one previously-minted token by id.
|
|
41
41
|
Idempotent: revoking twice still returns success.
|
|
42
42
|
|
|
43
|
-
list <
|
|
43
|
+
list <attachment-id> Enumerate the tokens minted against one attachment,
|
|
44
44
|
including revoked rows (for audit). Returns
|
|
45
|
-
{
|
|
45
|
+
{ attachment_id, items: [...] } where each item carries
|
|
46
46
|
{ token_id, token_prefix, expires_at, once,
|
|
47
47
|
created_at, last_used_at, use_count, revoked_at }.
|
|
48
48
|
The token plaintext is NEVER returned.
|
|
@@ -56,10 +56,10 @@ Options:
|
|
|
56
56
|
|
|
57
57
|
Output: stdout is machine-readable JSON.`;
|
|
58
58
|
async function runBlobTokenMint(args) {
|
|
59
|
-
assertKnownFlags(args, MINT_FLAGS, MINT_BOOLS, "pane
|
|
60
|
-
const
|
|
61
|
-
if (!
|
|
62
|
-
fail("missing <
|
|
59
|
+
assertKnownFlags(args, MINT_FLAGS, MINT_BOOLS, "pane attachment token mint");
|
|
60
|
+
const attachmentId = args.positionals[1];
|
|
61
|
+
if (!attachmentId) {
|
|
62
|
+
fail("missing <attachment-id> — 'pane attachment token mint <attachment-id>'", "invalid_args");
|
|
63
63
|
}
|
|
64
64
|
const ttlRaw = args.flags.get("ttl");
|
|
65
65
|
const ttl = ttlRaw === undefined ? undefined : Number(ttlRaw);
|
|
@@ -68,7 +68,7 @@ async function runBlobTokenMint(args) {
|
|
|
68
68
|
}
|
|
69
69
|
const client = makeClient(args);
|
|
70
70
|
try {
|
|
71
|
-
const r = await client.mintBlobToken(
|
|
71
|
+
const r = await client.mintBlobToken(attachmentId, {
|
|
72
72
|
ttlSeconds: ttl,
|
|
73
73
|
once: args.bools.has("once"),
|
|
74
74
|
});
|
|
@@ -79,15 +79,15 @@ async function runBlobTokenMint(args) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
async function runBlobTokenRevoke(args) {
|
|
82
|
-
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane
|
|
83
|
-
const
|
|
82
|
+
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane attachment token revoke");
|
|
83
|
+
const attachmentId = args.positionals[1];
|
|
84
84
|
const tokenId = args.positionals[2];
|
|
85
|
-
if (!
|
|
86
|
-
fail("missing arguments — 'pane
|
|
85
|
+
if (!attachmentId || !tokenId) {
|
|
86
|
+
fail("missing arguments — 'pane attachment token revoke <attachment-id> <token-id>'", "invalid_args");
|
|
87
87
|
}
|
|
88
88
|
const client = makeClient(args);
|
|
89
89
|
try {
|
|
90
|
-
const r = await client.revokeBlobToken(
|
|
90
|
+
const r = await client.revokeBlobToken(attachmentId, tokenId);
|
|
91
91
|
printJson(r);
|
|
92
92
|
}
|
|
93
93
|
catch (e) {
|
|
@@ -95,14 +95,14 @@ async function runBlobTokenRevoke(args) {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
async function runBlobTokenList(args) {
|
|
98
|
-
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane
|
|
99
|
-
const
|
|
100
|
-
if (!
|
|
101
|
-
fail("missing <
|
|
98
|
+
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane attachment token list");
|
|
99
|
+
const attachmentId = args.positionals[1];
|
|
100
|
+
if (!attachmentId) {
|
|
101
|
+
fail("missing <attachment-id> — 'pane attachment token list <attachment-id>'", "invalid_args");
|
|
102
102
|
}
|
|
103
103
|
const client = makeClient(args);
|
|
104
104
|
try {
|
|
105
|
-
const r = await client.listBlobTokens(
|
|
105
|
+
const r = await client.listBlobTokens(attachmentId);
|
|
106
106
|
printJson(r);
|
|
107
107
|
}
|
|
108
108
|
catch (e) {
|
|
@@ -111,7 +111,7 @@ async function runBlobTokenList(args) {
|
|
|
111
111
|
}
|
|
112
112
|
export async function runBlobToken(args) {
|
|
113
113
|
// positionals[0] is the verb (mint | revoke | list), positionals[1..] are
|
|
114
|
-
// the verb's args. (The
|
|
114
|
+
// the verb's args. (The attachment.ts dispatcher already shifted off the "token"
|
|
115
115
|
// marker before calling us.)
|
|
116
116
|
const verb = args.positionals[0];
|
|
117
117
|
switch (verb) {
|
|
@@ -125,9 +125,9 @@ export async function runBlobToken(args) {
|
|
|
125
125
|
await runBlobTokenList(args);
|
|
126
126
|
break;
|
|
127
127
|
case undefined:
|
|
128
|
-
fail("missing verb — usage: pane
|
|
128
|
+
fail("missing verb — usage: pane attachment token <mint|revoke|list> (run 'pane attachment token --help')", "invalid_args");
|
|
129
129
|
break;
|
|
130
130
|
default:
|
|
131
|
-
fail(`unknown token verb '${verb}' — expected mint|revoke|list (run 'pane
|
|
131
|
+
fail(`unknown token verb '${verb}' — expected mint|revoke|list (run 'pane attachment token --help')`, "invalid_args");
|
|
132
132
|
}
|
|
133
133
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane attachment upload` — POST /v1/attachments (multipart), three scopes.
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
4
|
import { assertKnownFlags } from "../argv.js";
|
|
@@ -7,24 +7,24 @@ import { fail, failFromError, printJson } from "../output.js";
|
|
|
7
7
|
const KNOWN_FLAGS = [
|
|
8
8
|
"file",
|
|
9
9
|
"scope",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
10
|
+
"surface-id",
|
|
11
|
+
"template-id",
|
|
12
12
|
"filename",
|
|
13
13
|
"mime",
|
|
14
14
|
];
|
|
15
15
|
const KNOWN_BOOLS = [];
|
|
16
|
-
export const blobUploadHelp = `pane
|
|
16
|
+
export const blobUploadHelp = `pane attachment upload — upload a local file as a attachment
|
|
17
17
|
|
|
18
18
|
Usage:
|
|
19
|
-
pane
|
|
19
|
+
pane attachment upload --file <path> [options]
|
|
20
20
|
|
|
21
21
|
Required:
|
|
22
22
|
--file <path> Local file to upload.
|
|
23
23
|
|
|
24
24
|
Scope (default: agent):
|
|
25
|
-
--scope <s> "agent" | "
|
|
26
|
-
--
|
|
27
|
-
--
|
|
25
|
+
--scope <s> "agent" | "surface" | "template".
|
|
26
|
+
--surface-id <id> Required when --scope=surface.
|
|
27
|
+
--template-id <id> Required when --scope=template.
|
|
28
28
|
|
|
29
29
|
Optional:
|
|
30
30
|
--filename <name> Display filename (otherwise basename of --file).
|
|
@@ -35,12 +35,12 @@ Optional:
|
|
|
35
35
|
-h, --help Show this help.
|
|
36
36
|
|
|
37
37
|
Output (stdout, JSON):
|
|
38
|
-
|
|
38
|
+
AttachmentRef — { attachment_id, scope, mime, size, sha256, ... }`;
|
|
39
39
|
export async function runBlobUpload(args) {
|
|
40
|
-
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane
|
|
40
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane attachment upload");
|
|
41
41
|
const filePath = args.flags.get("file");
|
|
42
42
|
if (!filePath) {
|
|
43
|
-
fail("missing --file <path> — 'pane
|
|
43
|
+
fail("missing --file <path> — 'pane attachment upload' requires a local file to upload", "invalid_args");
|
|
44
44
|
}
|
|
45
45
|
let bytes;
|
|
46
46
|
try {
|
|
@@ -51,23 +51,23 @@ export async function runBlobUpload(args) {
|
|
|
51
51
|
}
|
|
52
52
|
const scopeRaw = args.flags.get("scope") ?? "agent";
|
|
53
53
|
if (scopeRaw !== "agent" &&
|
|
54
|
-
scopeRaw !== "
|
|
55
|
-
scopeRaw !== "
|
|
56
|
-
fail(`unknown --scope '${scopeRaw}' — expected one of: agent,
|
|
54
|
+
scopeRaw !== "surface" &&
|
|
55
|
+
scopeRaw !== "template") {
|
|
56
|
+
fail(`unknown --scope '${scopeRaw}' — expected one of: agent, surface, template`, "invalid_args");
|
|
57
57
|
}
|
|
58
58
|
const scope = scopeRaw;
|
|
59
|
-
if (scope === "
|
|
60
|
-
fail("--scope=
|
|
59
|
+
if (scope === "surface" && !args.flags.get("surface-id")) {
|
|
60
|
+
fail("--scope=surface requires --surface-id <id>", "invalid_args");
|
|
61
61
|
}
|
|
62
|
-
if (scope === "
|
|
63
|
-
fail("--scope=
|
|
62
|
+
if (scope === "template" && !args.flags.get("template-id")) {
|
|
63
|
+
fail("--scope=template requires --template-id <id>", "invalid_args");
|
|
64
64
|
}
|
|
65
65
|
const client = makeClient(args);
|
|
66
66
|
try {
|
|
67
67
|
const ref = await client.uploadBlob(bytes, {
|
|
68
68
|
scope,
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
surfaceId: args.flags.get("surface-id"),
|
|
70
|
+
templateId: args.flags.get("template-id"),
|
|
71
71
|
filename: args.flags.get("filename") ?? basename(filePath),
|
|
72
72
|
mime: args.flags.get("mime"),
|
|
73
73
|
});
|
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane attachment` — manage binary attachments (attachments) on the relay.
|
|
2
2
|
//
|
|
3
|
-
// A
|
|
4
|
-
// agent and optionally bound to a
|
|
5
|
-
// by id with `format: pane-
|
|
3
|
+
// A attachment is a typed binary file (image, PDF, audio, video, etc.) owned by an
|
|
4
|
+
// agent and optionally bound to a surface or template. Pages reference attachments
|
|
5
|
+
// by id with `format: pane-attachment-id`; participants can fetch a attachment through a
|
|
6
6
|
// minted capability URL (/b/<token>) without needing the agent's API key.
|
|
7
7
|
//
|
|
8
8
|
// This file is a thin dispatcher — each verb's actual logic lives in its own
|
|
9
|
-
// file (
|
|
10
|
-
// the token sub-noun is dispatched via
|
|
9
|
+
// file (attachment-upload.ts, attachment-download.ts, attachment-show.ts, attachment-delete.ts) and
|
|
10
|
+
// the token sub-noun is dispatched via attachment-token.ts.
|
|
11
11
|
//
|
|
12
|
-
// Most
|
|
12
|
+
// Most attachment verbs read their primary positional (the attachment_id) at
|
|
13
13
|
// positionals[0]; we slice off our own verb before delegating so each verb
|
|
14
|
-
// runner doesn't need to know it was reached through `pane
|
|
15
|
-
import { runBlobUpload, blobUploadHelp } from "./
|
|
16
|
-
import { runBlobDownload, blobDownloadHelp } from "./
|
|
17
|
-
import { runBlobShow, blobShowHelp } from "./
|
|
18
|
-
import { runBlobList, blobListHelp } from "./
|
|
19
|
-
import { runBlobDelete, blobDeleteHelp } from "./
|
|
20
|
-
import { runBlobToken, blobTokenHelp } from "./
|
|
14
|
+
// runner doesn't need to know it was reached through `pane attachment`.
|
|
15
|
+
import { runBlobUpload, blobUploadHelp } from "./attachment-upload.js";
|
|
16
|
+
import { runBlobDownload, blobDownloadHelp } from "./attachment-download.js";
|
|
17
|
+
import { runBlobShow, blobShowHelp } from "./attachment-show.js";
|
|
18
|
+
import { runBlobList, blobListHelp } from "./attachment-list.js";
|
|
19
|
+
import { runBlobDelete, blobDeleteHelp } from "./attachment-delete.js";
|
|
20
|
+
import { runBlobToken, blobTokenHelp } from "./attachment-token.js";
|
|
21
21
|
import { fail } from "../output.js";
|
|
22
|
-
export const blobHelp = `pane
|
|
22
|
+
export const blobHelp = `pane attachment — manage attachments (binary attachments) on the relay
|
|
23
23
|
|
|
24
|
-
A
|
|
24
|
+
A attachment is a typed binary file (image, PDF, audio, video, ...) the agent has
|
|
25
25
|
uploaded to the relay. Blobs are scoped:
|
|
26
26
|
|
|
27
|
-
agent — reusable across the agent's
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
agent — reusable across the agent's surfaces (default)
|
|
28
|
+
surface — bound to one surface; deleted with it
|
|
29
|
+
template — bound to a reusable template; deleted with it
|
|
30
30
|
|
|
31
|
-
Pages reference
|
|
32
|
-
\`format: pane-
|
|
33
|
-
agent's API key, mint a token with 'pane
|
|
31
|
+
Pages reference attachments by id (the relay's schema validates the id with
|
|
32
|
+
\`format: pane-attachment-id\`). For a participant-facing URL that bypasses the
|
|
33
|
+
agent's API key, mint a token with 'pane attachment token mint'.
|
|
34
34
|
|
|
35
35
|
Usage:
|
|
36
|
-
pane
|
|
36
|
+
pane attachment <verb> [options]
|
|
37
37
|
|
|
38
38
|
Verbs:
|
|
39
39
|
upload Upload a local file. Required: --file. Optional:
|
|
40
|
-
--scope, --
|
|
41
|
-
--mime. Prints {
|
|
40
|
+
--scope, --surface-id, --template-id, --filename,
|
|
41
|
+
--mime. Prints { attachment_id, scope, mime, size, sha256,
|
|
42
42
|
... }.
|
|
43
43
|
|
|
44
|
-
download <
|
|
44
|
+
download <attachment-id> Download a attachment by id. Use --out <path> to write a
|
|
45
45
|
file (default: writes to stdout — useful for piping).
|
|
46
46
|
|
|
47
|
-
show <
|
|
47
|
+
show <attachment-id> Print a attachment's metadata (HEAD-based — doesn't
|
|
48
48
|
download the bytes).
|
|
49
49
|
|
|
50
|
-
list Enumerate YOUR agent's non-deleted
|
|
50
|
+
list Enumerate YOUR agent's non-deleted attachments (newest
|
|
51
51
|
first). Supports --cursor + --limit for pagination.
|
|
52
52
|
|
|
53
|
-
delete <
|
|
53
|
+
delete <attachment-id> Soft-delete a attachment. Idempotent.
|
|
54
54
|
|
|
55
|
-
token <verb> Capability URLs for a
|
|
55
|
+
token <verb> Capability URLs for a attachment (mint | revoke | list).
|
|
56
56
|
'mint' returns a /b/<token> URL anyone can GET, with
|
|
57
57
|
optional --ttl and --once. 'revoke' invalidates one
|
|
58
|
-
token. 'list' enumerates a
|
|
58
|
+
token. 'list' enumerates a attachment's tokens (without
|
|
59
59
|
the token plaintext, which is unrecoverable).
|
|
60
60
|
|
|
61
|
-
Run \`pane
|
|
61
|
+
Run \`pane attachment <verb> --help\` for verb-specific options.
|
|
62
62
|
|
|
63
63
|
Output: stdout is machine-readable JSON. Errors go to stderr as
|
|
64
64
|
{"error":{"code","message"}} with a non-zero exit.`;
|
|
65
65
|
/**
|
|
66
66
|
* Build a new ParsedArgs with the leading positional (the verb) stripped.
|
|
67
|
-
* The downstream verb runners read their primary positional (the
|
|
67
|
+
* The downstream verb runners read their primary positional (the attachment_id)
|
|
68
68
|
* at positionals[0], so we hand them an args object that looks exactly like
|
|
69
|
-
* they were called directly — mirrors
|
|
69
|
+
* they were called directly — mirrors surface.ts's shiftPositionals.
|
|
70
70
|
*/
|
|
71
71
|
function shiftPositionals(args) {
|
|
72
72
|
// Propagate danglingValueFlags so the leaf runner's assertKnownFlags
|
|
73
73
|
// can still distinguish "unknown flag" from "missing value" — see the
|
|
74
|
-
// matching note in
|
|
74
|
+
// matching note in surface.ts's shiftPositionals.
|
|
75
75
|
const out = {
|
|
76
76
|
positionals: args.positionals.slice(1),
|
|
77
77
|
flags: args.flags,
|
|
@@ -84,7 +84,7 @@ function shiftPositionals(args) {
|
|
|
84
84
|
}
|
|
85
85
|
export async function runBlob(args) {
|
|
86
86
|
const verb = args.positionals[0];
|
|
87
|
-
// `pane
|
|
87
|
+
// `pane attachment token --help` (verb-level help on the token sub-noun, with no
|
|
88
88
|
// further sub-verb). The general --help pre-empt in index.ts only fires
|
|
89
89
|
// when no positional follows the noun; here a positional ("token") is
|
|
90
90
|
// present, so the sub-noun must own its own --help routing.
|
|
@@ -94,8 +94,8 @@ export async function runBlob(args) {
|
|
|
94
94
|
process.stdout.write(blobTokenHelp + "\n");
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
|
-
// `pane
|
|
98
|
-
// so the general pre-empt would already fire, but for parity with
|
|
97
|
+
// `pane attachment list --help` — same pattern (list takes no required positional
|
|
98
|
+
// so the general pre-empt would already fire, but for parity with surface.ts
|
|
99
99
|
// we route through here when args carry the "list" positional explicitly).
|
|
100
100
|
if (verb === "list" &&
|
|
101
101
|
args.bools.has("help") &&
|
|
@@ -124,10 +124,10 @@ export async function runBlob(args) {
|
|
|
124
124
|
await runBlobToken(inner);
|
|
125
125
|
break;
|
|
126
126
|
case undefined:
|
|
127
|
-
fail("missing verb — usage: pane
|
|
127
|
+
fail("missing verb — usage: pane attachment <upload|download|show|list|delete|token> (run 'pane attachment --help')", "invalid_args");
|
|
128
128
|
break;
|
|
129
129
|
default:
|
|
130
|
-
fail(`unknown
|
|
130
|
+
fail(`unknown attachment verb '${verb}' — expected upload|download|show|list|delete|token (run 'pane attachment --help')`, "invalid_args");
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
// Re-export per-verb helps so tests / docs can import them by canonical name
|