@paneui/cli 0.0.5 → 0.0.7
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 +22 -16
- package/dist/argv.js +95 -7
- package/dist/commands/agent.js +54 -0
- package/dist/commands/attachment-delete.js +37 -0
- package/dist/commands/attachment-download.js +53 -0
- package/dist/commands/attachment-list.js +50 -0
- package/dist/commands/attachment-show.js +37 -0
- package/dist/commands/attachment-token.js +133 -0
- package/dist/commands/attachment-upload.js +79 -0
- package/dist/commands/attachment.js +135 -0
- package/dist/commands/claim.js +68 -0
- package/dist/commands/config.js +26 -7
- package/dist/commands/create.js +120 -54
- package/dist/commands/delete.js +15 -11
- package/dist/commands/feedback.js +10 -4
- 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 +66 -12
- package/dist/commands/skill.js +20 -11
- package/dist/commands/state.js +16 -12
- package/dist/commands/surface.js +118 -0
- package/dist/commands/taste.js +22 -14
- package/dist/commands/{artifact.js → template.js} +94 -67
- package/dist/commands/watch.js +25 -21
- package/dist/config.js +4 -4
- package/dist/index.js +90 -85
- package/dist/input.js +4 -4
- package/dist/output.js +1 -1
- package/dist/store.js +2 -2
- package/dist/version.js +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// `pane attachment` — manage binary attachments (attachments) on the relay.
|
|
2
|
+
//
|
|
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
|
+
// 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 (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
|
+
//
|
|
12
|
+
// Most attachment verbs read their primary positional (the attachment_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 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
|
+
import { fail } from "../output.js";
|
|
22
|
+
export const blobHelp = `pane attachment — manage attachments (binary attachments) on the relay
|
|
23
|
+
|
|
24
|
+
A attachment 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 surfaces (default)
|
|
28
|
+
surface — bound to one surface; deleted with it
|
|
29
|
+
template — bound to a reusable template; deleted with it
|
|
30
|
+
|
|
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
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
pane attachment <verb> [options]
|
|
37
|
+
|
|
38
|
+
Verbs:
|
|
39
|
+
upload Upload a local file. Required: --file. Optional:
|
|
40
|
+
--scope, --surface-id, --template-id, --filename,
|
|
41
|
+
--mime. Prints { attachment_id, scope, mime, size, sha256,
|
|
42
|
+
... }.
|
|
43
|
+
|
|
44
|
+
download <attachment-id> Download a attachment by id. Use --out <path> to write a
|
|
45
|
+
file (default: writes to stdout — useful for piping).
|
|
46
|
+
|
|
47
|
+
show <attachment-id> Print a attachment's metadata (HEAD-based — doesn't
|
|
48
|
+
download the bytes).
|
|
49
|
+
|
|
50
|
+
list Enumerate YOUR agent's non-deleted attachments (newest
|
|
51
|
+
first). Supports --cursor + --limit for pagination.
|
|
52
|
+
|
|
53
|
+
delete <attachment-id> Soft-delete a attachment. Idempotent.
|
|
54
|
+
|
|
55
|
+
token <verb> Capability URLs for a attachment (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 attachment's tokens (without
|
|
59
|
+
the token plaintext, which is unrecoverable).
|
|
60
|
+
|
|
61
|
+
Run \`pane attachment <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 attachment_id)
|
|
68
|
+
* at positionals[0], so we hand them an args object that looks exactly like
|
|
69
|
+
* they were called directly — mirrors surface.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 surface.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 attachment 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 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
|
+
// 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 attachment <upload|download|show|list|delete|token> (run 'pane attachment --help')", "invalid_args");
|
|
128
|
+
break;
|
|
129
|
+
default:
|
|
130
|
+
fail(`unknown attachment verb '${verb}' — expected upload|download|show|list|delete|token (run 'pane attachment --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, };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// `pane agent claim <code>` — bind this agent to a human via a one-shot
|
|
2
|
+
// claim code the human generated in their settings UI.
|
|
3
|
+
//
|
|
4
|
+
// Flow (§6.1):
|
|
5
|
+
// 1. Alice opens Settings → "Claim an agent" → relay mints a one-shot code,
|
|
6
|
+
// shows it to her once, 15-min TTL.
|
|
7
|
+
// 2. Alice hands the code to the agent out-of-band (this CLI invocation
|
|
8
|
+
// is exactly that handoff).
|
|
9
|
+
// 3. CLI calls POST /v1/agents/claim with the calling agent's API key.
|
|
10
|
+
// 4. Relay binds Agent.ownerHumanId = alice.id, migrates surface ownership.
|
|
11
|
+
//
|
|
12
|
+
// The CLI does NOT print the human's email or id — only the relay's response,
|
|
13
|
+
// which is { ok, owner_human_id, claimed_at }. The agent's existing API key
|
|
14
|
+
// keeps working.
|
|
15
|
+
import { PaneClient, PaneApiError } from "@paneui/core";
|
|
16
|
+
import { assertKnownFlags } from "../argv.js";
|
|
17
|
+
import { resolveConfig } from "../config.js";
|
|
18
|
+
import { printJson, fail } from "../output.js";
|
|
19
|
+
const KNOWN_FLAGS = ["url", "api-key"];
|
|
20
|
+
const KNOWN_BOOLS = [];
|
|
21
|
+
export const claimHelp = `pane agent claim — claim this agent for a human
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
pane agent claim <code>
|
|
25
|
+
|
|
26
|
+
Binds the calling agent to the human whose one-shot claim code is provided.
|
|
27
|
+
The human generates the code in their settings UI (or via the relay's
|
|
28
|
+
POST /v1/self/claim-codes endpoint) and hands it to the agent out-of-band.
|
|
29
|
+
|
|
30
|
+
Arguments:
|
|
31
|
+
<code> The one-shot claim code (begins with cc_). Required.
|
|
32
|
+
|
|
33
|
+
Options:
|
|
34
|
+
--url <url> Relay base URL. Falls back to PANE_URL / config file.
|
|
35
|
+
--api-key <key> Agent API key. Falls back to PANE_API_KEY / config file.
|
|
36
|
+
-h, --help Show this help.
|
|
37
|
+
|
|
38
|
+
Output (stdout, JSON):
|
|
39
|
+
{ ok: true, owner_human_id, claimed_at }
|
|
40
|
+
|
|
41
|
+
Errors:
|
|
42
|
+
invalid_code code is unknown, expired, or already consumed
|
|
43
|
+
agent_already_claimed this agent already has an owning human
|
|
44
|
+
|
|
45
|
+
Notes:
|
|
46
|
+
This is a one-way operation. To rotate the owner, revoke this agent
|
|
47
|
+
(\`pane key revoke\`) and register a new one.`;
|
|
48
|
+
export async function runClaim(args) {
|
|
49
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane agent claim");
|
|
50
|
+
const code = args.positionals[0];
|
|
51
|
+
if (!code) {
|
|
52
|
+
fail("missing required argument: <code> — run 'pane agent claim --help'", "invalid_args");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const creds = resolveConfig(args);
|
|
56
|
+
const client = new PaneClient({ url: creds.url, apiKey: creds.apiKey });
|
|
57
|
+
try {
|
|
58
|
+
const result = await client.claimAgent(code);
|
|
59
|
+
printJson(result);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err instanceof PaneApiError) {
|
|
63
|
+
fail(err.message, err.code);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
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
|
|
1
|
+
// `pane surface create` — create a surface via POST /v1/surfaces.
|
|
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
|
+
"template",
|
|
9
|
+
"template-id",
|
|
10
|
+
"template-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
|
|
@@ -10,9 +26,9 @@ import { printJson, fail, failFromError } from "../output.js";
|
|
|
10
26
|
//
|
|
11
27
|
// Match strategy: longest prefix wins. Schema paths whose top segment isn't
|
|
12
28
|
// in the table fall back to dotted notation so we degrade gracefully on
|
|
13
|
-
// fields that don't have a single corresponding flag (e.g. `
|
|
14
|
-
// — there's no single --
|
|
15
|
-
// --
|
|
29
|
+
// fields that don't have a single corresponding flag (e.g. `template.source`
|
|
30
|
+
// — there's no single --template-source flag for the inline form, just
|
|
31
|
+
// --template pointing at the whole attachment).
|
|
16
32
|
const SCHEMA_PATH_TO_FLAG = {
|
|
17
33
|
participants: "--participants",
|
|
18
34
|
"participants.humans": "--participants",
|
|
@@ -20,11 +36,13 @@ const SCHEMA_PATH_TO_FLAG = {
|
|
|
20
36
|
metadata: "--metadata",
|
|
21
37
|
callback: "--callback",
|
|
22
38
|
input_data: "--input-data",
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
39
|
+
title: "--title",
|
|
40
|
+
"template.id": "--template-id",
|
|
41
|
+
"template.version": "--version",
|
|
42
|
+
"template.type": "--template-type",
|
|
43
|
+
"template.source": "--template",
|
|
44
|
+
"template.event_schema": "--event-schema",
|
|
45
|
+
"template.input_schema": "--input-schema",
|
|
28
46
|
};
|
|
29
47
|
function schemaPathToFlag(path) {
|
|
30
48
|
const dotted = path.map(String).join(".");
|
|
@@ -39,32 +57,32 @@ function schemaPathToFlag(path) {
|
|
|
39
57
|
}
|
|
40
58
|
return dotted;
|
|
41
59
|
}
|
|
42
|
-
export const createHelp = `pane create — create a Pane
|
|
60
|
+
export const createHelp = `pane surface create — create a Pane surface
|
|
43
61
|
|
|
44
|
-
A
|
|
62
|
+
A surface is one use of an template. Supply the template in ONE of two ways:
|
|
45
63
|
|
|
46
|
-
Reference form — instance an existing reusable
|
|
64
|
+
Reference form — instance an existing reusable template (the cheap path,
|
|
47
65
|
no HTML re-sent):
|
|
48
|
-
pane create --
|
|
66
|
+
pane surface create --template-id <id|slug> [--version <n>] [--input-data <v>]
|
|
49
67
|
|
|
50
|
-
Inline form — a one-off
|
|
51
|
-
pane create --
|
|
68
|
+
Inline form — a one-off template, defined on this call:
|
|
69
|
+
pane surface create --template <path|inline> [--event-schema <path|json>] [options]
|
|
52
70
|
|
|
53
|
-
Exactly one of --
|
|
71
|
+
Exactly one of --template-id / --template must be given.
|
|
54
72
|
|
|
55
|
-
|
|
56
|
-
--
|
|
57
|
-
Tip: run 'pane
|
|
58
|
-
suitable
|
|
73
|
+
Template (choose one):
|
|
74
|
+
--template-id <v> Reference an existing named template by id or slug.
|
|
75
|
+
Tip: run 'pane template search <keywords>' first — a
|
|
76
|
+
suitable template may already exist; reuse it instead of
|
|
59
77
|
regenerating HTML.
|
|
60
|
-
--version <n> With --
|
|
61
|
-
the
|
|
62
|
-
--
|
|
63
|
-
HTML. Combine with --
|
|
78
|
+
--version <n> With --template-id: pin a specific version. Defaults to
|
|
79
|
+
the template's latest version.
|
|
80
|
+
--template <v> Inline HTML template. Either a file path / URL, or inline
|
|
81
|
+
HTML. Combine with --template-type to control reading.
|
|
64
82
|
--event-schema <v> Inline-form event schema. A .json file, or inline JSON.
|
|
65
|
-
Optional with --
|
|
83
|
+
Optional with --template. Omit for a view-only template
|
|
66
84
|
(a report/dashboard the human only views — no page/agent
|
|
67
|
-
events). Ignored with --
|
|
85
|
+
events). Ignored with --template-id.
|
|
68
86
|
|
|
69
87
|
Shape — an object with an "events" map, keyed by event
|
|
70
88
|
type. Each entry declares who may emit it and the JSON
|
|
@@ -84,17 +102,31 @@ 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 --template, rejected with --template-id
|
|
107
|
+
(the schema comes from the pinned template version
|
|
108
|
+
there). When present, the surface's --input-data is
|
|
109
|
+
validated against it AND any attachment ids declared at a
|
|
110
|
+
"format": "pane-attachment-id" site become reachable from the
|
|
111
|
+
page via window.pane.downloadBlob. Without it, attachment
|
|
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
|
+
--template-id references a named template, the relay
|
|
119
|
+
falls back to Template.name. Inline (--template …) form
|
|
120
|
+
always needs --title.
|
|
89
121
|
--input-data <v> This instance's seed data — a JSON object (file path or
|
|
90
|
-
inline JSON), validated by the relay against the
|
|
122
|
+
inline JSON), validated by the relay against the template
|
|
91
123
|
version's input_schema. The page reads it as
|
|
92
124
|
window.pane.inputData.
|
|
93
|
-
--
|
|
94
|
-
the --
|
|
95
|
-
does not serve "html-ref"
|
|
96
|
-
will reject the
|
|
97
|
-
--ttl <seconds>
|
|
125
|
+
--template-type <t> "html-inline" (default) or "html-ref". With "html-ref"
|
|
126
|
+
the --template value is treated as a URL. Note: the relay
|
|
127
|
+
does not serve "html-ref" templates in this release and
|
|
128
|
+
will reject the surface — use "html-inline".
|
|
129
|
+
--ttl <seconds> Surface time-to-live in seconds. The relay clamps this
|
|
98
130
|
to its configured MAX_TTL_SECONDS (defaults: 1 h
|
|
99
131
|
requested, 24 h max for self-host; hosted may differ).
|
|
100
132
|
The returned \`expires_at\` is the authoritative value.
|
|
@@ -106,18 +138,19 @@ Options:
|
|
|
106
138
|
-h, --help Show this help.
|
|
107
139
|
|
|
108
140
|
Output (stdout, JSON):
|
|
109
|
-
{
|
|
141
|
+
{ surface_id, urls, tokens, expires_at }
|
|
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) {
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
145
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane surface create");
|
|
146
|
+
const artifactIdVal = args.flags.get("template-id");
|
|
147
|
+
const artifactVal = args.flags.get("template");
|
|
148
|
+
// Exactly one of the two template forms must be present.
|
|
116
149
|
if (artifactIdVal !== undefined && artifactVal !== undefined) {
|
|
117
|
-
fail("pass only one of --
|
|
150
|
+
fail("pass only one of --template-id (reference an existing template) or --template (inline a one-off)", "invalid_args");
|
|
118
151
|
}
|
|
119
152
|
if (artifactIdVal === undefined && artifactVal === undefined) {
|
|
120
|
-
fail("missing
|
|
153
|
+
fail("missing template — pass --template-id <id|slug> to reference an existing template, or --template <path|inline> to inline one", "invalid_args");
|
|
121
154
|
}
|
|
122
155
|
// Assemble a candidate request object, then validate the whole thing with
|
|
123
156
|
// the shared Zod schema (single source of truth, matches what the relay
|
|
@@ -125,8 +158,12 @@ export async function runCreate(args) {
|
|
|
125
158
|
// flag-specific message; the schema then enforces shape and bounds.
|
|
126
159
|
const candidate = {};
|
|
127
160
|
if (artifactIdVal !== undefined) {
|
|
128
|
-
// Reference form — instance an existing named
|
|
129
|
-
// --event-schema are not
|
|
161
|
+
// Reference form — instance an existing named template. --template /
|
|
162
|
+
// --event-schema / --input-schema are not used here: the template's
|
|
163
|
+
// pinned version carries them already.
|
|
164
|
+
if (args.flags.get("input-schema") !== undefined) {
|
|
165
|
+
fail("--input-schema is incompatible with --template-id — the input schema comes from the pinned template version. Author the schema on the template (`pane template 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) {
|
|
@@ -136,32 +173,40 @@ export async function runCreate(args) {
|
|
|
136
173
|
}
|
|
137
174
|
ref["version"] = version;
|
|
138
175
|
}
|
|
139
|
-
candidate["
|
|
176
|
+
candidate["template"] = ref;
|
|
140
177
|
}
|
|
141
178
|
else {
|
|
142
|
-
// Inline form — the event
|
|
143
|
-
// relay transparently creates an anonymous
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
179
|
+
// Inline form — the event + input schemas ride inside the `template`
|
|
180
|
+
// object; the relay transparently creates an anonymous template 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 attachment ids in it are unreachable from
|
|
185
|
+
// the page (the participant attachment-download bridge walks input_data
|
|
186
|
+
// against the template version's inputSchema). Pass --input-schema
|
|
187
|
+
// when --input-data carries attachment refs the page needs to render.
|
|
188
|
+
// See #208.
|
|
147
189
|
const schemaVal = args.flags.get("event-schema");
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
190
|
+
const inputSchemaVal = args.flags.get("input-schema");
|
|
191
|
+
const templateType = (args.flags.get("template-type") ?? "html-inline");
|
|
192
|
+
if (templateType !== "html-inline" && templateType !== "html-ref") {
|
|
193
|
+
fail("--template-type must be 'html-inline' or 'html-ref'", "invalid_args");
|
|
151
194
|
}
|
|
152
195
|
// html-ref: the value is a URL, used verbatim. html-inline: file or literal.
|
|
153
196
|
let source;
|
|
154
197
|
try {
|
|
155
198
|
source =
|
|
156
|
-
|
|
199
|
+
templateType === "html-ref" ? artifactVal : resolveText(artifactVal);
|
|
157
200
|
}
|
|
158
201
|
catch (e) {
|
|
159
202
|
fail(e instanceof Error ? e.message : String(e), "invalid_args");
|
|
160
203
|
}
|
|
161
|
-
// Build the inline
|
|
162
|
-
// set to undefined) when
|
|
204
|
+
// Build the inline template 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 template / no input
|
|
207
|
+
// contract).
|
|
163
208
|
const inlineArtifact = {
|
|
164
|
-
type:
|
|
209
|
+
type: templateType,
|
|
165
210
|
source,
|
|
166
211
|
};
|
|
167
212
|
if (schemaVal !== undefined) {
|
|
@@ -172,7 +217,28 @@ export async function runCreate(args) {
|
|
|
172
217
|
fail(e instanceof Error ? e.message : String(e), "invalid_args");
|
|
173
218
|
}
|
|
174
219
|
}
|
|
175
|
-
|
|
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
|
+
}
|
|
232
|
+
candidate["template"] = inlineArtifact;
|
|
233
|
+
}
|
|
234
|
+
// --title — passthrough, no client-side requiredness. The relay is the
|
|
235
|
+
// single source of truth: it enforces "required, with --template-id +
|
|
236
|
+
// Template.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;
|
|
176
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).
|
package/dist/commands/delete.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
// `pane delete <id>` — close/delete a
|
|
1
|
+
// `pane surface delete <id>` — close/delete a surface.
|
|
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 surface delete — close/delete a surface
|
|
5
8
|
|
|
6
9
|
Usage:
|
|
7
|
-
pane delete <
|
|
10
|
+
pane surface delete <surface-id> [options]
|
|
8
11
|
|
|
9
|
-
Closes and deletes the
|
|
10
|
-
relay side — deleting an already-closed
|
|
12
|
+
Closes and deletes the surface (DELETE /v1/surfaces/:id). Idempotent on the
|
|
13
|
+
relay side — deleting an already-closed surface still succeeds.
|
|
11
14
|
|
|
12
15
|
Options:
|
|
13
16
|
--url <url> Relay base URL (overrides PANE_URL).
|
|
@@ -15,15 +18,16 @@ Options:
|
|
|
15
18
|
-h, --help Show this help.
|
|
16
19
|
|
|
17
20
|
Output (stdout, JSON):
|
|
18
|
-
{
|
|
21
|
+
{ surface_id, deleted: true }`;
|
|
19
22
|
export async function runDelete(args) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane surface delete");
|
|
24
|
+
const surfaceId = args.positionals[0];
|
|
25
|
+
if (!surfaceId)
|
|
26
|
+
fail("missing <surface-id>", "invalid_args");
|
|
23
27
|
const client = makeClient(args);
|
|
24
28
|
try {
|
|
25
|
-
await client.deleteSession(
|
|
26
|
-
printJson({
|
|
29
|
+
await client.deleteSession(surfaceId);
|
|
30
|
+
printJson({ surface_id: surfaceId, deleted: true });
|
|
27
31
|
}
|
|
28
32
|
catch (e) {
|
|
29
33
|
failFromError(e);
|
|
@@ -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", "surface-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
|
|
@@ -21,7 +25,7 @@ Options for 'create':
|
|
|
21
25
|
--type <bug|feature|note> Feedback category. Required.
|
|
22
26
|
--message <text|-> Message body. Pass '-' to read from stdin.
|
|
23
27
|
1..4000 chars after trim.
|
|
24
|
-
--
|
|
28
|
+
--surface-id <id> Optional surface this feedback relates to;
|
|
25
29
|
must belong to YOUR agent.
|
|
26
30
|
|
|
27
31
|
Options for 'list':
|
|
@@ -34,7 +38,7 @@ Global:
|
|
|
34
38
|
-h, --help Show this help.
|
|
35
39
|
|
|
36
40
|
Examples:
|
|
37
|
-
pane feedback create --type bug --message "watch hangs on empty
|
|
41
|
+
pane feedback create --type bug --message "watch hangs on empty surface"
|
|
38
42
|
echo "long-form note..." | pane feedback create --type note --message -
|
|
39
43
|
pane feedback list --limit 20
|
|
40
44
|
|
|
@@ -48,9 +52,10 @@ 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
|
-
const
|
|
58
|
+
const surfaceId = args.flags.get("surface-id");
|
|
54
59
|
if (type === undefined) {
|
|
55
60
|
fail("'pane feedback create' requires --type <bug|feature|note>", "invalid_args");
|
|
56
61
|
}
|
|
@@ -78,7 +83,7 @@ async function runFeedbackCreate(args) {
|
|
|
78
83
|
const res = await client.submitFeedback({
|
|
79
84
|
type: type,
|
|
80
85
|
message,
|
|
81
|
-
...(
|
|
86
|
+
...(surfaceId !== undefined ? { surfaceId } : {}),
|
|
82
87
|
});
|
|
83
88
|
printJson(res);
|
|
84
89
|
}
|
|
@@ -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;
|