@paneui/cli 0.0.4 → 0.0.6
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 +21 -15
- package/dist/argv.js +95 -7
- package/dist/commands/agent.js +41 -0
- package/dist/commands/artifact.js +31 -4
- package/dist/commands/blob-delete.js +37 -0
- package/dist/commands/blob-download.js +49 -0
- package/dist/commands/blob-list.js +50 -0
- package/dist/commands/blob-show.js +37 -0
- package/dist/commands/blob-token.js +133 -0
- package/dist/commands/blob-upload.js +79 -0
- package/dist/commands/blob.js +135 -0
- package/dist/commands/config.js +26 -7
- package/dist/commands/create.js +78 -12
- package/dist/commands/delete.js +7 -3
- package/dist/commands/feedback.js +6 -0
- package/dist/commands/{keys.js → key.js} +23 -17
- package/dist/commands/list.js +91 -0
- package/dist/commands/logout.js +10 -6
- package/dist/commands/participant.js +137 -0
- package/dist/commands/register.js +8 -4
- package/dist/commands/send.js +54 -6
- package/dist/commands/session.js +118 -0
- package/dist/commands/skill.js +20 -11
- package/dist/commands/state.js +9 -5
- package/dist/commands/taste.js +36 -19
- package/dist/commands/watch.js +10 -6
- package/dist/config.js +4 -4
- package/dist/index.js +85 -80
- package/dist/input.js +2 -2
- package/dist/output.js +1 -1
- package/dist/store.js +2 -2
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// `pane blob upload` — POST /v1/blobs (multipart), three scopes.
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
import { assertKnownFlags } from "../argv.js";
|
|
5
|
+
import { makeClient } from "../config.js";
|
|
6
|
+
import { fail, failFromError, printJson } from "../output.js";
|
|
7
|
+
const KNOWN_FLAGS = [
|
|
8
|
+
"file",
|
|
9
|
+
"scope",
|
|
10
|
+
"session-id",
|
|
11
|
+
"artifact-id",
|
|
12
|
+
"filename",
|
|
13
|
+
"mime",
|
|
14
|
+
];
|
|
15
|
+
const KNOWN_BOOLS = [];
|
|
16
|
+
export const blobUploadHelp = `pane blob upload — upload a local file as a blob
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
pane blob upload --file <path> [options]
|
|
20
|
+
|
|
21
|
+
Required:
|
|
22
|
+
--file <path> Local file to upload.
|
|
23
|
+
|
|
24
|
+
Scope (default: agent):
|
|
25
|
+
--scope <s> "agent" | "session" | "artifact".
|
|
26
|
+
--session-id <id> Required when --scope=session.
|
|
27
|
+
--artifact-id <id> Required when --scope=artifact.
|
|
28
|
+
|
|
29
|
+
Optional:
|
|
30
|
+
--filename <name> Display filename (otherwise basename of --file).
|
|
31
|
+
--mime <type> Declared Content-Type. The relay sniffs the bytes
|
|
32
|
+
regardless — this is advisory.
|
|
33
|
+
--url <url> Relay base URL (overrides PANE_URL).
|
|
34
|
+
--api-key <key> Agent API key (overrides PANE_API_KEY).
|
|
35
|
+
-h, --help Show this help.
|
|
36
|
+
|
|
37
|
+
Output (stdout, JSON):
|
|
38
|
+
BlobRef — { blob_id, scope, mime, size, sha256, ... }`;
|
|
39
|
+
export async function runBlobUpload(args) {
|
|
40
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane blob upload");
|
|
41
|
+
const filePath = args.flags.get("file");
|
|
42
|
+
if (!filePath) {
|
|
43
|
+
fail("missing --file <path> — 'pane blob upload' requires a local file to upload", "invalid_args");
|
|
44
|
+
}
|
|
45
|
+
let bytes;
|
|
46
|
+
try {
|
|
47
|
+
bytes = readFileSync(filePath);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
fail(`failed to read --file '${filePath}': ${e instanceof Error ? e.message : String(e)}`, "invalid_args");
|
|
51
|
+
}
|
|
52
|
+
const scopeRaw = args.flags.get("scope") ?? "agent";
|
|
53
|
+
if (scopeRaw !== "agent" &&
|
|
54
|
+
scopeRaw !== "session" &&
|
|
55
|
+
scopeRaw !== "artifact") {
|
|
56
|
+
fail(`unknown --scope '${scopeRaw}' — expected one of: agent, session, artifact`, "invalid_args");
|
|
57
|
+
}
|
|
58
|
+
const scope = scopeRaw;
|
|
59
|
+
if (scope === "session" && !args.flags.get("session-id")) {
|
|
60
|
+
fail("--scope=session requires --session-id <id>", "invalid_args");
|
|
61
|
+
}
|
|
62
|
+
if (scope === "artifact" && !args.flags.get("artifact-id")) {
|
|
63
|
+
fail("--scope=artifact requires --artifact-id <id>", "invalid_args");
|
|
64
|
+
}
|
|
65
|
+
const client = makeClient(args);
|
|
66
|
+
try {
|
|
67
|
+
const ref = await client.uploadBlob(bytes, {
|
|
68
|
+
scope,
|
|
69
|
+
sessionId: args.flags.get("session-id"),
|
|
70
|
+
artifactId: args.flags.get("artifact-id"),
|
|
71
|
+
filename: args.flags.get("filename") ?? basename(filePath),
|
|
72
|
+
mime: args.flags.get("mime"),
|
|
73
|
+
});
|
|
74
|
+
printJson(ref);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
failFromError(e);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// `pane blob` — manage binary attachments (blobs) on the relay.
|
|
2
|
+
//
|
|
3
|
+
// A blob is a typed binary file (image, PDF, audio, video, etc.) owned by an
|
|
4
|
+
// agent and optionally bound to a session or artifact. Pages reference blobs
|
|
5
|
+
// by id with `format: pane-blob-id`; participants can fetch a blob through a
|
|
6
|
+
// minted capability URL (/b/<token>) without needing the agent's API key.
|
|
7
|
+
//
|
|
8
|
+
// This file is a thin dispatcher — each verb's actual logic lives in its own
|
|
9
|
+
// file (blob-upload.ts, blob-download.ts, blob-show.ts, blob-delete.ts) and
|
|
10
|
+
// the token sub-noun is dispatched via blob-token.ts.
|
|
11
|
+
//
|
|
12
|
+
// Most blob verbs read their primary positional (the blob_id) at
|
|
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 blob`.
|
|
15
|
+
import { runBlobUpload, blobUploadHelp } from "./blob-upload.js";
|
|
16
|
+
import { runBlobDownload, blobDownloadHelp } from "./blob-download.js";
|
|
17
|
+
import { runBlobShow, blobShowHelp } from "./blob-show.js";
|
|
18
|
+
import { runBlobList, blobListHelp } from "./blob-list.js";
|
|
19
|
+
import { runBlobDelete, blobDeleteHelp } from "./blob-delete.js";
|
|
20
|
+
import { runBlobToken, blobTokenHelp } from "./blob-token.js";
|
|
21
|
+
import { fail } from "../output.js";
|
|
22
|
+
export const blobHelp = `pane blob — manage blobs (binary attachments) on the relay
|
|
23
|
+
|
|
24
|
+
A blob is a typed binary file (image, PDF, audio, video, ...) the agent has
|
|
25
|
+
uploaded to the relay. Blobs are scoped:
|
|
26
|
+
|
|
27
|
+
agent — reusable across the agent's sessions (default)
|
|
28
|
+
session — bound to one session; deleted with it
|
|
29
|
+
artifact — bound to a reusable artifact; deleted with it
|
|
30
|
+
|
|
31
|
+
Pages reference blobs by id (the relay's schema validates the id with
|
|
32
|
+
\`format: pane-blob-id\`). For a participant-facing URL that bypasses the
|
|
33
|
+
agent's API key, mint a token with 'pane blob token mint'.
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
pane blob <verb> [options]
|
|
37
|
+
|
|
38
|
+
Verbs:
|
|
39
|
+
upload Upload a local file. Required: --file. Optional:
|
|
40
|
+
--scope, --session-id, --artifact-id, --filename,
|
|
41
|
+
--mime. Prints { blob_id, scope, mime, size, sha256,
|
|
42
|
+
... }.
|
|
43
|
+
|
|
44
|
+
download <blob-id> Download a blob by id. Use --out <path> to write a
|
|
45
|
+
file (default: writes to stdout — useful for piping).
|
|
46
|
+
|
|
47
|
+
show <blob-id> Print a blob's metadata (HEAD-based — doesn't
|
|
48
|
+
download the bytes).
|
|
49
|
+
|
|
50
|
+
list Enumerate YOUR agent's non-deleted blobs (newest
|
|
51
|
+
first). Supports --cursor + --limit for pagination.
|
|
52
|
+
|
|
53
|
+
delete <blob-id> Soft-delete a blob. Idempotent.
|
|
54
|
+
|
|
55
|
+
token <verb> Capability URLs for a blob (mint | revoke | list).
|
|
56
|
+
'mint' returns a /b/<token> URL anyone can GET, with
|
|
57
|
+
optional --ttl and --once. 'revoke' invalidates one
|
|
58
|
+
token. 'list' enumerates a blob's tokens (without
|
|
59
|
+
the token plaintext, which is unrecoverable).
|
|
60
|
+
|
|
61
|
+
Run \`pane blob <verb> --help\` for verb-specific options.
|
|
62
|
+
|
|
63
|
+
Output: stdout is machine-readable JSON. Errors go to stderr as
|
|
64
|
+
{"error":{"code","message"}} with a non-zero exit.`;
|
|
65
|
+
/**
|
|
66
|
+
* Build a new ParsedArgs with the leading positional (the verb) stripped.
|
|
67
|
+
* The downstream verb runners read their primary positional (the blob_id)
|
|
68
|
+
* at positionals[0], so we hand them an args object that looks exactly like
|
|
69
|
+
* they were called directly — mirrors session.ts's shiftPositionals.
|
|
70
|
+
*/
|
|
71
|
+
function shiftPositionals(args) {
|
|
72
|
+
// Propagate danglingValueFlags so the leaf runner's assertKnownFlags
|
|
73
|
+
// can still distinguish "unknown flag" from "missing value" — see the
|
|
74
|
+
// matching note in session.ts's shiftPositionals.
|
|
75
|
+
const out = {
|
|
76
|
+
positionals: args.positionals.slice(1),
|
|
77
|
+
flags: args.flags,
|
|
78
|
+
bools: args.bools,
|
|
79
|
+
};
|
|
80
|
+
if (args.danglingValueFlags !== undefined) {
|
|
81
|
+
out.danglingValueFlags = args.danglingValueFlags;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
export async function runBlob(args) {
|
|
86
|
+
const verb = args.positionals[0];
|
|
87
|
+
// `pane blob token --help` (verb-level help on the token sub-noun, with no
|
|
88
|
+
// further sub-verb). The general --help pre-empt in index.ts only fires
|
|
89
|
+
// when no positional follows the noun; here a positional ("token") is
|
|
90
|
+
// present, so the sub-noun must own its own --help routing.
|
|
91
|
+
if (verb === "token" &&
|
|
92
|
+
args.bools.has("help") &&
|
|
93
|
+
args.positionals.length === 1) {
|
|
94
|
+
process.stdout.write(blobTokenHelp + "\n");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// `pane blob list --help` — same pattern (list takes no required positional
|
|
98
|
+
// so the general pre-empt would already fire, but for parity with session.ts
|
|
99
|
+
// we route through here when args carry the "list" positional explicitly).
|
|
100
|
+
if (verb === "list" &&
|
|
101
|
+
args.bools.has("help") &&
|
|
102
|
+
args.positionals.length === 1) {
|
|
103
|
+
process.stdout.write(blobListHelp + "\n");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const inner = shiftPositionals(args);
|
|
107
|
+
switch (verb) {
|
|
108
|
+
case "upload":
|
|
109
|
+
await runBlobUpload(inner);
|
|
110
|
+
break;
|
|
111
|
+
case "download":
|
|
112
|
+
await runBlobDownload(inner);
|
|
113
|
+
break;
|
|
114
|
+
case "show":
|
|
115
|
+
await runBlobShow(inner);
|
|
116
|
+
break;
|
|
117
|
+
case "list":
|
|
118
|
+
await runBlobList(inner);
|
|
119
|
+
break;
|
|
120
|
+
case "delete":
|
|
121
|
+
await runBlobDelete(inner);
|
|
122
|
+
break;
|
|
123
|
+
case "token":
|
|
124
|
+
await runBlobToken(inner);
|
|
125
|
+
break;
|
|
126
|
+
case undefined:
|
|
127
|
+
fail("missing verb — usage: pane blob <upload|download|show|list|delete|token> (run 'pane blob --help')", "invalid_args");
|
|
128
|
+
break;
|
|
129
|
+
default:
|
|
130
|
+
fail(`unknown blob verb '${verb}' — expected upload|download|show|list|delete|token (run 'pane blob --help')`, "invalid_args");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Re-export per-verb helps so tests / docs can import them by canonical name
|
|
134
|
+
// without knowing which file owns each verb.
|
|
135
|
+
export { blobUploadHelp, blobDownloadHelp, blobShowHelp, blobListHelp, blobDeleteHelp, blobTokenHelp, };
|
package/dist/commands/config.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
// `pane config` — show the resolved relay config without a network call.
|
|
1
|
+
// `pane config show` — show the resolved relay config without a network call.
|
|
2
|
+
import { assertKnownFlags } from "../argv.js";
|
|
2
3
|
import { describeConfig } from "../config.js";
|
|
3
|
-
import { printJson } from "../output.js";
|
|
4
|
+
import { printJson, fail } from "../output.js";
|
|
5
|
+
const NO_FLAGS = [];
|
|
6
|
+
const NO_BOOLS = [];
|
|
4
7
|
export const configHelp = `pane config — show the resolved relay config
|
|
5
8
|
|
|
6
9
|
Usage:
|
|
7
|
-
pane config [options]
|
|
10
|
+
pane config show [options]
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
Verbs:
|
|
13
|
+
show Print the relay URL and API-key info the CLI would use,
|
|
14
|
+
and where each value came from. Makes NO network call —
|
|
15
|
+
purely inspects flags, env vars, and the saved config
|
|
16
|
+
file.
|
|
12
17
|
|
|
13
18
|
The API key is never printed in full: only a short masked prefix.
|
|
14
19
|
|
|
@@ -26,6 +31,20 @@ Output (stdout, JSON):
|
|
|
26
31
|
key_source, "flag" | "env" | "store" | "none"
|
|
27
32
|
config_path absolute path to the CLI config file
|
|
28
33
|
}`;
|
|
29
|
-
|
|
34
|
+
async function runConfigShow(args) {
|
|
35
|
+
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane config show");
|
|
30
36
|
printJson(describeConfig(args));
|
|
31
37
|
}
|
|
38
|
+
export async function runConfig(args) {
|
|
39
|
+
const verb = args.positionals[0];
|
|
40
|
+
switch (verb) {
|
|
41
|
+
case "show":
|
|
42
|
+
await runConfigShow(args);
|
|
43
|
+
break;
|
|
44
|
+
case undefined:
|
|
45
|
+
fail("missing verb — usage: pane config show (run 'pane config --help')", "invalid_args");
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
fail(`unknown config verb '${verb}' — expected show (run 'pane config --help')`, "invalid_args");
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/commands/create.js
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
|
-
// `pane create` — create a session via POST /v1/sessions.
|
|
1
|
+
// `pane session create` — create a session via POST /v1/sessions.
|
|
2
2
|
import { createSessionSchema } from "@paneui/core";
|
|
3
|
+
import { assertKnownFlags } from "../argv.js";
|
|
3
4
|
import { makeClient } from "../config.js";
|
|
4
5
|
import { resolveJson, resolveText } from "../input.js";
|
|
5
6
|
import { printJson, fail, failFromError } from "../output.js";
|
|
7
|
+
const KNOWN_FLAGS = [
|
|
8
|
+
"artifact",
|
|
9
|
+
"artifact-id",
|
|
10
|
+
"artifact-type",
|
|
11
|
+
"version",
|
|
12
|
+
"event-schema",
|
|
13
|
+
"input-schema",
|
|
14
|
+
"title",
|
|
15
|
+
"input-data",
|
|
16
|
+
"ttl",
|
|
17
|
+
"participants",
|
|
18
|
+
"metadata",
|
|
19
|
+
"callback",
|
|
20
|
+
];
|
|
21
|
+
const KNOWN_BOOLS = [];
|
|
6
22
|
// Translate a Zod schema path (e.g. ["participants","humans"]) back to the
|
|
7
23
|
// public CLI flag the user actually typed. Without this, a `--participants 0`
|
|
8
24
|
// rejection surfaces as `participants.humans: ...` — which leaks the wire
|
|
@@ -20,11 +36,13 @@ const SCHEMA_PATH_TO_FLAG = {
|
|
|
20
36
|
metadata: "--metadata",
|
|
21
37
|
callback: "--callback",
|
|
22
38
|
input_data: "--input-data",
|
|
39
|
+
title: "--title",
|
|
23
40
|
"artifact.id": "--artifact-id",
|
|
24
41
|
"artifact.version": "--version",
|
|
25
42
|
"artifact.type": "--artifact-type",
|
|
26
43
|
"artifact.source": "--artifact",
|
|
27
44
|
"artifact.event_schema": "--event-schema",
|
|
45
|
+
"artifact.input_schema": "--input-schema",
|
|
28
46
|
};
|
|
29
47
|
function schemaPathToFlag(path) {
|
|
30
48
|
const dotted = path.map(String).join(".");
|
|
@@ -39,16 +57,16 @@ function schemaPathToFlag(path) {
|
|
|
39
57
|
}
|
|
40
58
|
return dotted;
|
|
41
59
|
}
|
|
42
|
-
export const createHelp = `pane create — create a Pane session
|
|
60
|
+
export const createHelp = `pane session create — create a Pane session
|
|
43
61
|
|
|
44
62
|
A session is one use of an artifact. Supply the artifact in ONE of two ways:
|
|
45
63
|
|
|
46
64
|
Reference form — instance an existing reusable artifact (the cheap path,
|
|
47
65
|
no HTML re-sent):
|
|
48
|
-
pane create --artifact-id <id|slug> [--version <n>] [--input-data <v>]
|
|
66
|
+
pane session create --artifact-id <id|slug> [--version <n>] [--input-data <v>]
|
|
49
67
|
|
|
50
68
|
Inline form — a one-off artifact, defined on this call:
|
|
51
|
-
pane create --artifact <path|inline> [--event-schema <path|json>] [options]
|
|
69
|
+
pane session create --artifact <path|inline> [--event-schema <path|json>] [options]
|
|
52
70
|
|
|
53
71
|
Exactly one of --artifact-id / --artifact must be given.
|
|
54
72
|
|
|
@@ -84,8 +102,22 @@ Artifact (choose one):
|
|
|
84
102
|
emittedBy is any non-empty subset of ["page", "agent"].
|
|
85
103
|
payload is a JSON Schema; the relay validates every
|
|
86
104
|
emit against it. See docs/SPEC.md for the full grammar.
|
|
105
|
+
--input-schema <v> Inline-form input schema. A .json file, or inline JSON.
|
|
106
|
+
Optional with --artifact, rejected with --artifact-id
|
|
107
|
+
(the schema comes from the pinned artifact version
|
|
108
|
+
there). When present, the session's --input-data is
|
|
109
|
+
validated against it AND any blob ids declared at a
|
|
110
|
+
"format": "pane-blob-id" site become reachable from the
|
|
111
|
+
page via window.pane.downloadBlob. Without it, blob
|
|
112
|
+
refs in --input-data are silently unreachable. See
|
|
113
|
+
docs/SPEC.md and #208.
|
|
87
114
|
|
|
88
115
|
Options:
|
|
116
|
+
--title <text> Tab title shown to the human (max 80 chars, single
|
|
117
|
+
line). Required, with one ergonomic exception: when
|
|
118
|
+
--artifact-id references a named artifact, the relay
|
|
119
|
+
falls back to Artifact.name. Inline (--artifact …) form
|
|
120
|
+
always needs --title.
|
|
89
121
|
--input-data <v> This instance's seed data — a JSON object (file path or
|
|
90
122
|
inline JSON), validated by the relay against the artifact
|
|
91
123
|
version's input_schema. The page reads it as
|
|
@@ -110,6 +142,7 @@ Output (stdout, JSON):
|
|
|
110
142
|
|
|
111
143
|
Deliver urls.humans to the human(s); keep tokens.agent for the WS stream.`;
|
|
112
144
|
export async function runCreate(args) {
|
|
145
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane session create");
|
|
113
146
|
const artifactIdVal = args.flags.get("artifact-id");
|
|
114
147
|
const artifactVal = args.flags.get("artifact");
|
|
115
148
|
// Exactly one of the two artifact forms must be present.
|
|
@@ -126,7 +159,11 @@ export async function runCreate(args) {
|
|
|
126
159
|
const candidate = {};
|
|
127
160
|
if (artifactIdVal !== undefined) {
|
|
128
161
|
// Reference form — instance an existing named artifact. --artifact /
|
|
129
|
-
// --event-schema are not
|
|
162
|
+
// --event-schema / --input-schema are not used here: the artifact's
|
|
163
|
+
// pinned version carries them already.
|
|
164
|
+
if (args.flags.get("input-schema") !== undefined) {
|
|
165
|
+
fail("--input-schema is incompatible with --artifact-id — the input schema comes from the pinned artifact version. Author the schema on the artifact (`pane artifact create --input-schema …`) instead.", "invalid_args");
|
|
166
|
+
}
|
|
130
167
|
const ref = { id: artifactIdVal };
|
|
131
168
|
const versionRaw = args.flags.get("version");
|
|
132
169
|
if (versionRaw !== undefined) {
|
|
@@ -139,12 +176,18 @@ export async function runCreate(args) {
|
|
|
139
176
|
candidate["artifact"] = ref;
|
|
140
177
|
}
|
|
141
178
|
else {
|
|
142
|
-
// Inline form — the event
|
|
143
|
-
// relay transparently creates an anonymous artifact behind
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
179
|
+
// Inline form — the event + input schemas ride inside the `artifact`
|
|
180
|
+
// object; the relay transparently creates an anonymous artifact behind
|
|
181
|
+
// it. Both schemas are optional:
|
|
182
|
+
// - --event-schema absent → view-only one-off (no page/agent emits)
|
|
183
|
+
// - --input-schema absent → no input contract; --input-data passes
|
|
184
|
+
// through unvalidated AND any blob ids in it are unreachable from
|
|
185
|
+
// the page (the participant blob-download bridge walks input_data
|
|
186
|
+
// against the artifact version's inputSchema). Pass --input-schema
|
|
187
|
+
// when --input-data carries blob refs the page needs to render.
|
|
188
|
+
// See #208.
|
|
147
189
|
const schemaVal = args.flags.get("event-schema");
|
|
190
|
+
const inputSchemaVal = args.flags.get("input-schema");
|
|
148
191
|
const artifactType = (args.flags.get("artifact-type") ?? "html-inline");
|
|
149
192
|
if (artifactType !== "html-inline" && artifactType !== "html-ref") {
|
|
150
193
|
fail("--artifact-type must be 'html-inline' or 'html-ref'", "invalid_args");
|
|
@@ -158,8 +201,10 @@ export async function runCreate(args) {
|
|
|
158
201
|
catch (e) {
|
|
159
202
|
fail(e instanceof Error ? e.message : String(e), "invalid_args");
|
|
160
203
|
}
|
|
161
|
-
// Build the inline artifact object. event_schema
|
|
162
|
-
// set to undefined) when
|
|
204
|
+
// Build the inline artifact object. event_schema / input_schema are
|
|
205
|
+
// OMITTED entirely (not set to undefined) when their flags are absent —
|
|
206
|
+
// omission is meaningful at the relay (view-only artifact / no input
|
|
207
|
+
// contract).
|
|
163
208
|
const inlineArtifact = {
|
|
164
209
|
type: artifactType,
|
|
165
210
|
source,
|
|
@@ -172,8 +217,29 @@ export async function runCreate(args) {
|
|
|
172
217
|
fail(e instanceof Error ? e.message : String(e), "invalid_args");
|
|
173
218
|
}
|
|
174
219
|
}
|
|
220
|
+
if (inputSchemaVal !== undefined) {
|
|
221
|
+
try {
|
|
222
|
+
const v = resolveJson(inputSchemaVal, "--input-schema");
|
|
223
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) {
|
|
224
|
+
fail("--input-schema must be a JSON object", "invalid_args");
|
|
225
|
+
}
|
|
226
|
+
inlineArtifact["input_schema"] = v;
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
fail(e instanceof Error ? e.message : String(e), "invalid_args");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
175
232
|
candidate["artifact"] = inlineArtifact;
|
|
176
233
|
}
|
|
234
|
+
// --title — passthrough, no client-side requiredness. The relay is the
|
|
235
|
+
// single source of truth: it enforces "required, with --artifact-id +
|
|
236
|
+
// Artifact.name as the only fallback" and the shape rules (length, control
|
|
237
|
+
// chars). Keeping all that server-side avoids drift between the CLI's
|
|
238
|
+
// pre-checks and the relay's actual rules.
|
|
239
|
+
const titleRaw = args.flags.get("title");
|
|
240
|
+
if (titleRaw !== undefined) {
|
|
241
|
+
candidate["title"] = titleRaw;
|
|
242
|
+
}
|
|
177
243
|
// --input-data — per-instance seed data, applies to either form (the relay
|
|
178
244
|
// validates it against the pinned version's input_schema).
|
|
179
245
|
const inputDataRaw = args.flags.get("input-data");
|
package/dist/commands/delete.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
// `pane delete <id>` — close/delete a session.
|
|
1
|
+
// `pane session delete <id>` — close/delete a session.
|
|
2
|
+
import { assertKnownFlags } from "../argv.js";
|
|
2
3
|
import { makeClient } from "../config.js";
|
|
3
4
|
import { printJson, fail, failFromError } from "../output.js";
|
|
4
|
-
|
|
5
|
+
const KNOWN_FLAGS = [];
|
|
6
|
+
const KNOWN_BOOLS = [];
|
|
7
|
+
export const deleteHelp = `pane session delete — close/delete a session
|
|
5
8
|
|
|
6
9
|
Usage:
|
|
7
|
-
pane delete <session-id> [options]
|
|
10
|
+
pane session delete <session-id> [options]
|
|
8
11
|
|
|
9
12
|
Closes and deletes the session (DELETE /v1/sessions/:id). Idempotent on the
|
|
10
13
|
relay side — deleting an already-closed session still succeeds.
|
|
@@ -17,6 +20,7 @@ Options:
|
|
|
17
20
|
Output (stdout, JSON):
|
|
18
21
|
{ session_id, deleted: true }`;
|
|
19
22
|
export async function runDelete(args) {
|
|
23
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane session delete");
|
|
20
24
|
const sessionId = args.positionals[0];
|
|
21
25
|
if (!sessionId)
|
|
22
26
|
fail("missing <session-id>", "invalid_args");
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { assertKnownFlags } from "../argv.js";
|
|
1
2
|
import { makeClient } from "../config.js";
|
|
2
3
|
import { printJson, fail, failFromError } from "../output.js";
|
|
4
|
+
const CREATE_FLAGS = ["type", "message", "session-id"];
|
|
5
|
+
const LIST_FLAGS = ["limit", "before"];
|
|
6
|
+
const NO_BOOLS = [];
|
|
3
7
|
export const feedbackHelp = `pane feedback — submit / list feedback to the relay operator
|
|
4
8
|
|
|
5
9
|
Feedback is a one-shot bug report, feature request, or note from YOUR agent
|
|
@@ -48,6 +52,7 @@ async function readStdin() {
|
|
|
48
52
|
return Buffer.concat(chunks).toString("utf8");
|
|
49
53
|
}
|
|
50
54
|
async function runFeedbackCreate(args) {
|
|
55
|
+
assertKnownFlags(args, CREATE_FLAGS, NO_BOOLS, "pane feedback create");
|
|
51
56
|
const type = args.flags.get("type");
|
|
52
57
|
const rawMessage = args.flags.get("message");
|
|
53
58
|
const sessionId = args.flags.get("session-id");
|
|
@@ -87,6 +92,7 @@ async function runFeedbackCreate(args) {
|
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
async function runFeedbackList(args) {
|
|
95
|
+
assertKnownFlags(args, LIST_FLAGS, NO_BOOLS, "pane feedback list");
|
|
90
96
|
const limitRaw = args.flags.get("limit");
|
|
91
97
|
const before = args.flags.get("before");
|
|
92
98
|
let limit;
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane key` — inspect or revoke the calling agent's API key.
|
|
2
2
|
//
|
|
3
|
-
// Flat command namespace: `
|
|
4
|
-
// positional
|
|
3
|
+
// Flat command namespace: `key` is one top-level noun that branches on a
|
|
4
|
+
// positional verb (list / revoke). The relay scopes /v1/keys to the
|
|
5
5
|
// authenticated agent, so there is exactly one key — the caller's own. Both
|
|
6
|
-
//
|
|
6
|
+
// verbs therefore act ONLY on the caller's own key.
|
|
7
|
+
import { assertKnownFlags } from "../argv.js";
|
|
7
8
|
import { makeClient } from "../config.js";
|
|
8
9
|
import { printJson, fail, failFromError } from "../output.js";
|
|
9
|
-
|
|
10
|
+
const NO_FLAGS = [];
|
|
11
|
+
const NO_BOOLS = [];
|
|
12
|
+
const REVOKE_BOOLS = ["yes"];
|
|
13
|
+
export const keyHelp = `pane key — inspect or revoke YOUR agent's API key
|
|
10
14
|
|
|
11
15
|
Usage:
|
|
12
|
-
pane
|
|
16
|
+
pane key <verb> [options]
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
Verbs:
|
|
15
19
|
list Show YOUR agent's key info. The relay scopes keys to the
|
|
16
20
|
authenticated agent — there is exactly one key per agent, your
|
|
17
21
|
own. Prints { agent_id, name, key_prefix, created_at,
|
|
@@ -19,18 +23,19 @@ Subcommands:
|
|
|
19
23
|
|
|
20
24
|
revoke Revoke YOUR OWN API key — a self-destruct. The key stops working
|
|
21
25
|
IMMEDIATELY; every subsequent command fails until you run
|
|
22
|
-
'pane register' again to provision a new key. The relay only
|
|
26
|
+
'pane agent register' again to provision a new key. The relay only
|
|
23
27
|
allows revoking your own key. Requires --yes to confirm.
|
|
24
28
|
Prints { revoked: true, agent_id }.
|
|
25
29
|
|
|
26
30
|
Options:
|
|
27
|
-
--yes Confirm '
|
|
31
|
+
--yes Confirm 'key revoke' (required — it is irreversible).
|
|
28
32
|
--url <url> Relay base URL (overrides PANE_URL).
|
|
29
33
|
--api-key <key> Agent API key (overrides PANE_API_KEY).
|
|
30
34
|
-h, --help Show this help.
|
|
31
35
|
|
|
32
36
|
Output: stdout is machine-readable JSON.`;
|
|
33
|
-
async function
|
|
37
|
+
async function runKeyList(args) {
|
|
38
|
+
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane key list");
|
|
34
39
|
const client = makeClient(args);
|
|
35
40
|
try {
|
|
36
41
|
const info = await client.listKeys();
|
|
@@ -40,9 +45,10 @@ async function runKeysList(args) {
|
|
|
40
45
|
failFromError(e);
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
|
-
async function
|
|
48
|
+
async function runKeyRevoke(args) {
|
|
49
|
+
assertKnownFlags(args, NO_FLAGS, REVOKE_BOOLS, "pane key revoke");
|
|
44
50
|
if (!args.bools.has("yes")) {
|
|
45
|
-
fail("'pane
|
|
51
|
+
fail("'pane key revoke' revokes YOUR OWN API key — it stops working " +
|
|
46
52
|
"immediately and is irreversible. Pass --yes to confirm.", "confirmation_required");
|
|
47
53
|
}
|
|
48
54
|
const client = makeClient(args);
|
|
@@ -58,19 +64,19 @@ async function runKeysRevoke(args) {
|
|
|
58
64
|
failFromError(e);
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
|
-
export async function
|
|
67
|
+
export async function runKey(args) {
|
|
62
68
|
const sub = args.positionals[0];
|
|
63
69
|
switch (sub) {
|
|
64
70
|
case "list":
|
|
65
|
-
await
|
|
71
|
+
await runKeyList(args);
|
|
66
72
|
break;
|
|
67
73
|
case "revoke":
|
|
68
|
-
await
|
|
74
|
+
await runKeyRevoke(args);
|
|
69
75
|
break;
|
|
70
76
|
case undefined:
|
|
71
|
-
fail("missing
|
|
77
|
+
fail("missing verb — usage: pane key <list|revoke> (run 'pane key --help')", "invalid_args");
|
|
72
78
|
break;
|
|
73
79
|
default:
|
|
74
|
-
fail(`unknown
|
|
80
|
+
fail(`unknown key verb '${sub}' — expected list|revoke (run 'pane key --help')`, "invalid_args");
|
|
75
81
|
}
|
|
76
82
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// `pane session list` — enumerate YOUR agent's sessions.
|
|
2
|
+
//
|
|
3
|
+
// The recovery primitive when the create-response was dropped: the URL itself
|
|
4
|
+
// is unrecoverable (the relay stores only the token hash), but every other
|
|
5
|
+
// field of the session is intact and listable here. Pair with
|
|
6
|
+
// `pane session participant new` to mint a fresh URL on a session whose
|
|
7
|
+
// original was lost.
|
|
8
|
+
import { assertKnownFlags } from "../argv.js";
|
|
9
|
+
import { makeClient } from "../config.js";
|
|
10
|
+
import { printJson, fail, failFromError } from "../output.js";
|
|
11
|
+
const KNOWN_FLAGS = ["status", "limit", "cursor", "artifact-id"];
|
|
12
|
+
const KNOWN_BOOLS = [];
|
|
13
|
+
export const listHelp = `pane session list — list YOUR agent's sessions
|
|
14
|
+
|
|
15
|
+
Prints sessions (newest first) with no secrets in the response. Participant
|
|
16
|
+
tokens are stored hashed and CANNOT be recovered — if you lost a session URL,
|
|
17
|
+
mint a fresh one with 'pane session participant new <session-id>'.
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
pane session list [options]
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--status <s> open | closed | all. Default: open. The status reported
|
|
24
|
+
is the EFFECTIVE status — a row whose ttl is in the past
|
|
25
|
+
is reported as 'closed' even if not yet swept.
|
|
26
|
+
--limit <N> Page size (default 50, max 200).
|
|
27
|
+
--cursor <c> Opaque cursor from a previous page's next_cursor.
|
|
28
|
+
--artifact-id <i> Filter to sessions instantiated from a specific named
|
|
29
|
+
artifact (head id; not version id). Inline (anonymous)
|
|
30
|
+
artifacts cannot be filtered this way — they have no
|
|
31
|
+
stable handle.
|
|
32
|
+
--url <url> Relay base URL (overrides PANE_URL).
|
|
33
|
+
--api-key <key> Agent API key (overrides PANE_API_KEY).
|
|
34
|
+
-h, --help Show this help.
|
|
35
|
+
|
|
36
|
+
Recovery recipe (lost the URL but the session is still alive):
|
|
37
|
+
pane session list # find the
|
|
38
|
+
# session_id +
|
|
39
|
+
# participant_id
|
|
40
|
+
# you lost
|
|
41
|
+
pane session participant new <session-id> # mint a fresh URL
|
|
42
|
+
pane session participant revoke <session-id> <p-id> # invalidate the
|
|
43
|
+
# old URL
|
|
44
|
+
|
|
45
|
+
Output (stdout, JSON):
|
|
46
|
+
{
|
|
47
|
+
items: [
|
|
48
|
+
{
|
|
49
|
+
session_id, title, status, artifact_id, artifact_version_id,
|
|
50
|
+
artifact_version, participants: [...], created_at, expires_at,
|
|
51
|
+
has_callback
|
|
52
|
+
},
|
|
53
|
+
...
|
|
54
|
+
],
|
|
55
|
+
next_cursor: <opaque|null>
|
|
56
|
+
}`;
|
|
57
|
+
const STATUSES = ["open", "closed", "all"];
|
|
58
|
+
export async function runList(args) {
|
|
59
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane session list");
|
|
60
|
+
const opts = {};
|
|
61
|
+
const status = args.flags.get("status");
|
|
62
|
+
if (status !== undefined) {
|
|
63
|
+
if (!STATUSES.includes(status)) {
|
|
64
|
+
fail(`--status must be one of: ${STATUSES.join(", ")} (got '${status}')`, "invalid_args");
|
|
65
|
+
}
|
|
66
|
+
opts.status = status;
|
|
67
|
+
}
|
|
68
|
+
const limitRaw = args.flags.get("limit");
|
|
69
|
+
if (limitRaw !== undefined) {
|
|
70
|
+
const n = Number(limitRaw);
|
|
71
|
+
if (!Number.isInteger(n) || n <= 0 || n > 200) {
|
|
72
|
+
fail(`--limit must be an integer in 1..200 (got '${limitRaw}')`, "invalid_args");
|
|
73
|
+
}
|
|
74
|
+
opts.limit = n;
|
|
75
|
+
}
|
|
76
|
+
const cursor = args.flags.get("cursor");
|
|
77
|
+
if (cursor !== undefined && cursor !== "")
|
|
78
|
+
opts.cursor = cursor;
|
|
79
|
+
const artifactId = args.flags.get("artifact-id");
|
|
80
|
+
if (artifactId !== undefined && artifactId !== "") {
|
|
81
|
+
opts.artifact_id = artifactId;
|
|
82
|
+
}
|
|
83
|
+
const client = makeClient(args);
|
|
84
|
+
try {
|
|
85
|
+
const page = await client.listSessions(opts);
|
|
86
|
+
printJson(page);
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
failFromError(e);
|
|
90
|
+
}
|
|
91
|
+
}
|