@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.
@@ -0,0 +1,118 @@
1
+ // `pane surface` — the central noun of pane: open, observe, send to, and
2
+ // close a surface.
3
+ //
4
+ // A surface is one *use* of an template: an open URL the human(s) interact
5
+ // with, plus an event log the agent reads and appends to. Every other noun
6
+ // (template, attachment, key, taste, feedback) exists in service of surfaces.
7
+ //
8
+ // This file is a thin dispatcher — each verb's actual logic lives in its own
9
+ // file (create.ts, state.ts, send.ts, watch.ts, delete.ts). The verb runners
10
+ // expect the surface id at positionals[0]; we slice off our own verb before
11
+ // delegating so they don't need to know they're being called via `surface`.
12
+ import { runCreate } from "./create.js";
13
+ import { runState } from "./state.js";
14
+ import { runSend } from "./send.js";
15
+ import { runWatch } from "./watch.js";
16
+ import { runDelete } from "./delete.js";
17
+ import { runList, listHelp } from "./list.js";
18
+ import { runParticipant, participantHelp } from "./participant.js";
19
+ import { fail } from "../output.js";
20
+ export const sessionHelp = `pane surface — open, observe, send to, and close surfaces
21
+
22
+ A surface is one use of an template: an open URL the human(s) interact with,
23
+ plus an event log the agent reads and appends to.
24
+
25
+ Usage:
26
+ pane surface <verb> [options]
27
+
28
+ Verbs:
29
+ create Create a surface (POST /v1/surfaces). Prints surface_id,
30
+ urls, tokens, expires_at.
31
+ list Enumerate YOUR agent's surfaces. The recovery primitive
32
+ for "I dropped the create response" — surfaces are
33
+ listable, but participant tokens are stored hashed and
34
+ CANNOT be recovered. Use 'participant new' to mint a
35
+ fresh URL.
36
+ show <id> Non-blocking snapshot: surface metadata + event log.
37
+ Supports --wait <secs> for relay-side long-polling.
38
+ send <id> Emit an agent event into a surface.
39
+ watch <id> Stream a surface's events as JSON-lines on stdout
40
+ (long-lived; the building block for pipe-readers).
41
+ delete <id> Close/delete a surface (DELETE /v1/surfaces/:id).
42
+ participant List / mint / revoke participant URLs on an existing
43
+ <list|new|revoke> surface. 'list' returns the participant ids you need
44
+ for 'revoke'; 'new' replaces the destructive 'delete
45
+ + recreate' workaround for a lost URL; 'revoke'
46
+ invalidates one URL without touching the surface.
47
+
48
+ Run \`pane surface <verb> --help\` for verb-specific options.`;
49
+ /**
50
+ * Build a new ParsedArgs with the leading positional (the verb) stripped.
51
+ * The downstream verb runners (runState / runSend / runWatch / runDelete)
52
+ * read the surface id at positionals[0], so we hand them an args object that
53
+ * looks exactly like the pre-restructure invocation.
54
+ */
55
+ function shiftPositionals(args) {
56
+ // Propagate danglingValueFlags too — otherwise the leaf runner's
57
+ // assertKnownFlags can't tell that the user wrote `--title` without a
58
+ // value, and falls through to a less-useful downstream error.
59
+ const out = {
60
+ positionals: args.positionals.slice(1),
61
+ flags: args.flags,
62
+ bools: args.bools,
63
+ };
64
+ if (args.danglingValueFlags !== undefined) {
65
+ out.danglingValueFlags = args.danglingValueFlags;
66
+ }
67
+ return out;
68
+ }
69
+ export async function runSession(args) {
70
+ const verb = args.positionals[0];
71
+ // `pane surface participant --help` (verb-level help on the participant
72
+ // sub-noun, with no further sub-verb). The general --help pre-empt in
73
+ // index.ts only fires when no positional follows the noun; here a
74
+ // positional ("participant") is present, so the sub-noun must own its own
75
+ // --help routing.
76
+ if (verb === "participant" &&
77
+ args.bools.has("help") &&
78
+ args.positionals.length === 1) {
79
+ process.stdout.write(participantHelp + "\n");
80
+ return;
81
+ }
82
+ // `pane surface list --help` — same pattern.
83
+ if (verb === "list" &&
84
+ args.bools.has("help") &&
85
+ args.positionals.length === 1) {
86
+ process.stdout.write(listHelp + "\n");
87
+ return;
88
+ }
89
+ const inner = shiftPositionals(args);
90
+ switch (verb) {
91
+ case "create":
92
+ await runCreate(inner);
93
+ break;
94
+ case "list":
95
+ await runList(inner);
96
+ break;
97
+ case "show":
98
+ await runState(inner);
99
+ break;
100
+ case "send":
101
+ await runSend(inner);
102
+ break;
103
+ case "watch":
104
+ await runWatch(inner);
105
+ break;
106
+ case "delete":
107
+ await runDelete(inner);
108
+ break;
109
+ case "participant":
110
+ await runParticipant(inner);
111
+ break;
112
+ case undefined:
113
+ fail("missing verb — usage: pane surface <create|list|show|send|watch|delete|participant> (run 'pane surface --help')", "invalid_args");
114
+ break;
115
+ default:
116
+ fail(`unknown surface verb '${verb}' — expected create|list|show|send|watch|delete|participant (run 'pane surface --help')`, "invalid_args");
117
+ }
118
+ }
@@ -1,46 +1,51 @@
1
1
  // `pane taste` — read / write / clear the calling agent's freeform "taste
2
- // notes" markdown blob.
2
+ // notes" markdown attachment.
3
3
  //
4
4
  // Taste notes are presentation preferences the agent has learned from human
5
5
  // feedback ("denser layout", "no rounded corners", "use a dark header") — the
6
- // kind of guidance that should outlive a single session. The intended loop:
6
+ // kind of guidance that should outlive a single surface. The intended loop:
7
7
  //
8
- // 1. Before generating a pane artifact, run `pane taste get` and feed the
8
+ // 1. Before generating a pane template, run `pane taste get` and feed the
9
9
  // `taste` field into the prompt so prior preferences shape the output.
10
10
  // 2. When the human gives new presentation feedback, run `pane taste get`,
11
11
  // merge the feedback into the existing notes IN THE PROMPT, then call
12
- // `pane taste set` with the WHOLE new blob (the relay does whole-blob
12
+ // `pane taste set` with the WHOLE new attachment (the relay does whole-attachment
13
13
  // replace, not append — that's deliberate, so the notes can't grow
14
14
  // unbounded into noise).
15
15
  //
16
16
  // Keep taste notes about *presentation/UI taste only* — colours, density,
17
- // component preferences. Project context, todos, and per-session state belong
18
- // somewhere else. Today the blob is keyed by the agent's API key (per-agent);
17
+ // component preferences. Project context, todos, and per-surface state belong
18
+ // somewhere else. Today the attachment is keyed by the agent's API key (per-agent);
19
19
  // when pane gains first-class humans, this may move to per-human.
20
20
  import { readFileSync } from "node:fs";
21
+ import { assertKnownFlags } from "../argv.js";
21
22
  import { makeClient } from "../config.js";
22
23
  import { printJson, fail, failFromError } from "../output.js";
24
+ const NO_FLAGS = [];
25
+ const NO_BOOLS = [];
26
+ const SET_FLAGS = ["file"];
27
+ const CLEAR_BOOLS = ["yes"];
23
28
  export const tasteHelp = `pane taste — read / write / clear YOUR agent's UI taste notes
24
29
 
25
- Taste notes are a small markdown blob storing presentation preferences your
30
+ Taste notes are a small markdown attachment storing presentation preferences your
26
31
  agent has picked up from human feedback ("denser table", "no rounded corners",
27
- "use a dark header"). Read them before generating a pane artifact so prior
32
+ "use a dark header"). Read them before generating a pane template so prior
28
33
  feedback shapes the output; rewrite them whenever the human gives new
29
34
  presentation feedback. Keep entries about UI/presentation taste only — not
30
- project context, todos, or session state.
35
+ project context, todos, or surface state.
31
36
 
32
37
  Usage:
33
38
  pane taste <subcommand> [options]
34
39
 
35
40
  Subcommands:
36
- get Print the current notes blob:
41
+ get Print the current notes attachment:
37
42
  { taste: string|null, updated_at: string|null, bytes: number }.
38
43
  taste is null and bytes is 0 when notes have never been written.
39
44
 
40
- set Whole-blob replace. Source the markdown via --file <path>,
45
+ set Whole-attachment replace. Source the markdown via --file <path>,
41
46
  --file - (read stdin), or by piping into 'pane taste set' with
42
47
  no flag. The relay rejects empty/whitespace-only payloads and
43
- caps the blob at MAX_TASTE_BYTES (utf8). To clear the notes,
48
+ caps the attachment at MAX_TASTE_BYTES (utf8). To clear the notes,
44
49
  use 'pane taste clear', not 'set' with an empty body.
45
50
 
46
51
  clear Delete the notes. Requires --yes (it is destructive). Prints
@@ -74,6 +79,7 @@ async function readStdin() {
74
79
  return Buffer.concat(chunks).toString("utf8");
75
80
  }
76
81
  async function runTasteGet(args) {
82
+ assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane taste get");
77
83
  const client = makeClient(args);
78
84
  try {
79
85
  const info = await client.getTaste();
@@ -84,8 +90,9 @@ async function runTasteGet(args) {
84
90
  }
85
91
  }
86
92
  async function runTasteSet(args) {
93
+ assertKnownFlags(args, SET_FLAGS, NO_BOOLS, "pane taste set");
87
94
  const filePath = args.flags.get("file");
88
- // Source the blob deterministically — no isTTY-flag fusing, because
95
+ // Source the attachment deterministically — no isTTY-flag fusing, because
89
96
  // `!process.stdin.isTTY` is true under every non-interactive caller
90
97
  // (pipes, redirects, closed fd, CI, agent harnesses) and would wrongly
91
98
  // reject `--file` for the entire target audience. See issue #148.
@@ -112,7 +119,7 @@ async function runTasteSet(args) {
112
119
  fail("'pane taste set' needs input — pass --file <path>, pipe markdown on stdin, or use --file -", "invalid_args");
113
120
  }
114
121
  if (taste.trim().length === 0) {
115
- fail("'pane taste set' refuses an empty or whitespace-only blob — use 'pane taste clear --yes' to delete the notes", "invalid_args");
122
+ fail("'pane taste set' refuses an empty or whitespace-only attachment — use 'pane taste clear --yes' to delete the notes", "invalid_args");
116
123
  }
117
124
  const client = makeClient(args);
118
125
  try {
@@ -124,6 +131,7 @@ async function runTasteSet(args) {
124
131
  }
125
132
  }
126
133
  async function runTasteClear(args) {
134
+ assertKnownFlags(args, NO_FLAGS, CLEAR_BOOLS, "pane taste clear");
127
135
  if (!args.bools.has("yes")) {
128
136
  fail("'pane taste clear' deletes YOUR agent's taste notes — it is destructive. Pass --yes to confirm.", "confirmation_required");
129
137
  }
@@ -1,80 +1,101 @@
1
- // `pane artifact` — manage reusable, versioned artifacts.
1
+ // `pane template` — manage reusable, versioned templates.
2
2
  //
3
- // Flat command namespace: `artifact` is one top-level command that branches on
3
+ // Flat command namespace: `template` is one top-level command that branches on
4
4
  // a positional subcommand (create / version / update / search / list / show /
5
5
  // delete).
6
- // An artifact is a reusable UI template (HTML + event schema + optional input
7
- // schema); a session is one *use* of one version of it. Authoring an artifact
8
- // once and instancing it via `pane create --artifact-id` removes the per-use
9
- // cost of regenerating the same HTML.
6
+ // An template is a reusable UI template (HTML + event schema + optional input
7
+ // schema); a surface is one *use* of one version of it. Authoring an template
8
+ // once and instancing it via `pane surface create --template-id` removes the
9
+ // per-use cost of regenerating the same HTML.
10
10
  import { createArtifactSchema, createArtifactVersionSchema, patchArtifactMetadataSchema, } from "@paneui/core";
11
+ import { assertKnownFlags } from "../argv.js";
11
12
  import { makeClient } from "../config.js";
12
13
  import { resolveJson, resolveText } from "../input.js";
13
14
  import { printJson, fail, failFromError } from "../output.js";
14
- export const artifactHelp = `pane artifact — manage reusable, versioned artifacts
15
+ const CREATE_FLAGS = [
16
+ "name",
17
+ "slug",
18
+ "description",
19
+ "tags",
20
+ "template",
21
+ "template-type",
22
+ "event-schema",
23
+ "input-schema",
24
+ ];
25
+ const VERSION_FLAGS = [
26
+ "template",
27
+ "template-type",
28
+ "event-schema",
29
+ "input-schema",
30
+ ];
31
+ const UPDATE_FLAGS = ["name", "slug", "description", "tags"];
32
+ const NO_FLAGS = [];
33
+ const NO_BOOLS = [];
34
+ const DELETE_BOOLS = ["yes"];
35
+ export const artifactHelp = `pane template — manage reusable, versioned templates
15
36
 
16
- An artifact is a reusable UI template: HTML + an event schema + an optional
17
- input schema. A session is one use of one version of it. Author an artifact
18
- once, then instance it many times with 'pane create --artifact-id <id|slug>'
19
- instead of regenerating the HTML on every session.
37
+ An template is a reusable UI template: HTML + an event schema + an optional
38
+ input schema. A surface is one use of one version of it. Author an template
39
+ once, then instance it many times with 'pane surface create --template-id <id|slug>'
40
+ instead of regenerating the HTML on every surface.
20
41
 
21
42
  Usage:
22
- pane artifact <subcommand> [options]
43
+ pane template <subcommand> [options]
23
44
 
24
45
  Subcommands:
25
- create Create a named, reusable artifact (its v1).
26
- version Append a new version to an existing artifact.
27
- update Update an artifact's head metadata (name/slug/description/tags).
28
- search Search the agent's named artifacts (lean — no HTML).
29
- list List the agent's named artifacts (search with no query).
30
- show Show a full artifact: head metadata + its version list.
31
- delete Permanently delete an artifact and ALL its versions. Requires
32
- --yes. Refused with 409 conflict if any session (open or
33
- closed) still references the artifact — delete those first.
46
+ create Create a named, reusable template (its v1).
47
+ version Append a new version to an existing template.
48
+ update Update an template's head metadata (name/slug/description/tags).
49
+ search Search the agent's named templates (lean — no HTML).
50
+ list List the agent's named templates (search with no query).
51
+ show Show a full template: head metadata + its version list.
52
+ delete Permanently delete an template and ALL its versions. Requires
53
+ --yes. Refused with 409 conflict if any surface (open or
54
+ closed) still references the template — delete those first.
34
55
 
35
- pane artifact create --name <n> --artifact <path|inline>
56
+ pane template create --name <n> --template <path|inline>
36
57
  [--event-schema <path|json>] [--slug <s>]
37
58
  [--description <d>] [--tags <t1,t2>]
38
- [--input-schema <path|json>] [--artifact-type <t>]
39
- Creates a named artifact. Prints { artifact_id, slug, version }.
59
+ [--input-schema <path|json>] [--template-type <t>]
60
+ Creates a named template. Prints { template_id, slug, version }.
40
61
 
41
- pane artifact version <id|slug> --artifact <path|inline>
62
+ pane template version <id|slug> --template <path|inline>
42
63
  [--event-schema <path|json>]
43
- [--input-schema <path|json>] [--artifact-type <t>]
44
- Appends a new immutable version. Prints { artifact_id, version }.
64
+ [--input-schema <path|json>] [--template-type <t>]
65
+ Appends a new immutable version. Prints { template_id, version }.
45
66
 
46
- pane artifact update <id|slug> [--name <n>] [--slug <s>]
67
+ pane template update <id|slug> [--name <n>] [--slug <s>]
47
68
  [--description <d>] [--tags <t1,t2>]
48
69
  Updates head metadata only (never the content). Prints the lean summary.
49
70
 
50
- pane artifact search [query]
71
+ pane template search [query]
51
72
  Text search over name + description + tags, ranked by last_used_at.
52
73
  Prints an array of { id, slug, name, description, tags,
53
74
  latest_version, last_used_at }.
54
75
 
55
- pane artifact list
56
- Alias of 'search' with no query — lists all the agent's artifacts.
76
+ pane template list
77
+ Alias of 'search' with no query — lists all the agent's templates.
57
78
 
58
- pane artifact show <id|slug>
59
- Prints the full artifact: head metadata + every version's content.
79
+ pane template show <id|slug>
80
+ Prints the full template: head metadata + every version's content.
60
81
 
61
- pane artifact delete <id|slug> --yes
62
- Permanently deletes the artifact and all its versions. Refused
63
- (409 conflict) if any session in any state still references one
64
- of the artifact's versions — run 'pane delete <session-id>' on
82
+ pane template delete <id|slug> --yes
83
+ Permanently deletes the template and all its versions. Refused
84
+ (409 conflict) if any surface in any state still references one
85
+ of the template's versions — run 'pane surface delete <surface-id>' on
65
86
  each first, or wait for the relay's TTL sweeper to reclaim them.
66
- Prints { artifact, deleted: true } on success.
87
+ Prints { template, deleted: true } on success.
67
88
 
68
89
  Options:
69
- --name <n> Artifact display name (required for 'create').
90
+ --name <n> Template display name (required for 'create').
70
91
  --slug <s> Stable, agent-chosen handle (unique per agent). The
71
- durable way to reference the artifact later.
72
- --description <d> Prose: what the artifact is and does. Read by an agent
92
+ durable way to reference the template later.
93
+ --description <d> Prose: what the template is and does. Read by an agent
73
94
  deciding whether to reuse it.
74
95
  --tags <t1,t2,...> Comma-separated keywords for search.
75
- --artifact <v> HTML artifact body — a file path, or inline HTML.
96
+ --template <v> HTML template body — a file path, or inline HTML.
76
97
  --event-schema <v> Event schema — a .json file path, or inline JSON.
77
- Optional: omit for a view-only artifact (a
98
+ Optional: omit for a view-only template (a
78
99
  report/dashboard the human only views — no page/agent
79
100
  events).
80
101
 
@@ -96,27 +117,27 @@ Options:
96
117
  emittedBy is any non-empty subset of ["page", "agent"].
97
118
  payload is a JSON Schema; the relay validates every
98
119
  emit against it. See docs/SPEC.md for the full grammar.
99
- --input-schema <v> JSON Schema for this artifact's per-session input_data —
120
+ --input-schema <v> JSON Schema for this template's per-surface input_data —
100
121
  a file path, or inline JSON. Optional.
101
- --artifact-type <t> "html-inline" (default) or "html-ref".
122
+ --template-type <t> "html-inline" (default) or "html-ref".
102
123
  --url <url> Relay base URL (overrides PANE_URL).
103
124
  --api-key <key> Agent API key (overrides PANE_API_KEY).
104
125
  -h, --help Show this help.
105
126
 
106
127
  Output: stdout is machine-readable JSON.`;
107
- /** Resolve --artifact-type, defaulting to html-inline. */
128
+ /** Resolve --template-type, defaulting to html-inline. */
108
129
  function resolveArtifactType(args) {
109
- const t = (args.flags.get("artifact-type") ?? "html-inline");
130
+ const t = (args.flags.get("template-type") ?? "html-inline");
110
131
  if (t !== "html-inline" && t !== "html-ref") {
111
- fail("--artifact-type must be 'html-inline' or 'html-ref'", "invalid_args");
132
+ fail("--template-type must be 'html-inline' or 'html-ref'", "invalid_args");
112
133
  }
113
134
  return t;
114
135
  }
115
- /** Resolve the artifact HTML body (file or inline; verbatim URL for html-ref). */
136
+ /** Resolve the template HTML body (file or inline; verbatim URL for html-ref). */
116
137
  function resolveSource(args, type) {
117
- const artifactVal = args.flags.get("artifact");
138
+ const artifactVal = args.flags.get("template");
118
139
  if (!artifactVal)
119
- fail("missing --artifact", "invalid_args");
140
+ fail("missing --template", "invalid_args");
120
141
  try {
121
142
  return type === "html-ref" ? artifactVal : resolveText(artifactVal);
122
143
  }
@@ -127,7 +148,7 @@ function resolveSource(args, type) {
127
148
  /**
128
149
  * Resolve --event-schema — file path or inline JSON. --event-schema is
129
150
  * optional: when absent this returns `undefined`, which makes a view-only
130
- * artifact (no event vocabulary — the human only views it). The caller must
151
+ * template (no event vocabulary — the human only views it). The caller must
131
152
  * omit `event_schema` from the request entirely when this returns `undefined`.
132
153
  */
133
154
  function resolveEventSchema(args) {
@@ -169,6 +190,7 @@ function resolveTags(args) {
169
190
  return tags.length > 0 ? tags : undefined;
170
191
  }
171
192
  async function runArtifactCreate(args) {
193
+ assertKnownFlags(args, CREATE_FLAGS, NO_BOOLS, "pane template create");
172
194
  const name = args.flags.get("name");
173
195
  if (!name)
174
196
  fail("missing --name", "invalid_args");
@@ -185,7 +207,7 @@ async function runArtifactCreate(args) {
185
207
  type,
186
208
  };
187
209
  // event_schema is OMITTED entirely when --event-schema is absent — a view-only
188
- // artifact. Setting it to `undefined` would still add the key.
210
+ // template. Setting it to `undefined` would still add the key.
189
211
  if (eventSchema !== undefined)
190
212
  candidate["event_schema"] = eventSchema;
191
213
  if (slug !== undefined)
@@ -200,14 +222,14 @@ async function runArtifactCreate(args) {
200
222
  if (!parsed.success) {
201
223
  const issue = parsed.error.issues[0];
202
224
  const where = issue && issue.path.length > 0 ? issue.path.join(".") : "request";
203
- fail(`invalid artifact: ${where}: ${issue ? issue.message : "validation failed"}`, "invalid_args", parsed.error.flatten());
225
+ fail(`invalid template: ${where}: ${issue ? issue.message : "validation failed"}`, "invalid_args", parsed.error.flatten());
204
226
  }
205
227
  const req = parsed.data;
206
228
  const client = makeClient(args);
207
229
  try {
208
230
  const res = await client.createArtifact(req);
209
231
  printJson({
210
- artifact_id: res.artifact_id,
232
+ template_id: res.template_id,
211
233
  slug: slug ?? null,
212
234
  version: res.version,
213
235
  });
@@ -217,9 +239,10 @@ async function runArtifactCreate(args) {
217
239
  }
218
240
  }
219
241
  async function runArtifactVersion(args) {
242
+ assertKnownFlags(args, VERSION_FLAGS, NO_BOOLS, "pane template version");
220
243
  const idOrSlug = args.positionals[1];
221
244
  if (!idOrSlug) {
222
- fail("missing artifact <id|slug> — usage: pane artifact version <id|slug>", "invalid_args");
245
+ fail("missing template <id|slug> — usage: pane template version <id|slug>", "invalid_args");
223
246
  }
224
247
  const type = resolveArtifactType(args);
225
248
  const source = resolveSource(args, type);
@@ -245,16 +268,17 @@ async function runArtifactVersion(args) {
245
268
  const client = makeClient(args);
246
269
  try {
247
270
  const res = await client.createArtifactVersion(idOrSlug, req);
248
- printJson({ artifact_id: res.artifact_id, version: res.version });
271
+ printJson({ template_id: res.template_id, version: res.version });
249
272
  }
250
273
  catch (e) {
251
274
  failFromError(e);
252
275
  }
253
276
  }
254
277
  async function runArtifactUpdate(args) {
278
+ assertKnownFlags(args, UPDATE_FLAGS, NO_BOOLS, "pane template update");
255
279
  const idOrSlug = args.positionals[1];
256
280
  if (!idOrSlug) {
257
- fail("missing artifact <id|slug> — usage: pane artifact update <id|slug>", "invalid_args");
281
+ fail("missing template <id|slug> — usage: pane template update <id|slug>", "invalid_args");
258
282
  }
259
283
  const candidate = {};
260
284
  const name = args.flags.get("name");
@@ -289,6 +313,7 @@ async function runArtifactUpdate(args) {
289
313
  }
290
314
  }
291
315
  async function runArtifactSearch(args, query) {
316
+ assertKnownFlags(args, NO_FLAGS, NO_BOOLS, query === undefined ? "pane template list" : "pane template search");
292
317
  const client = makeClient(args);
293
318
  try {
294
319
  const res = await client.searchArtifacts(query);
@@ -299,9 +324,10 @@ async function runArtifactSearch(args, query) {
299
324
  }
300
325
  }
301
326
  async function runArtifactShow(args) {
327
+ assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane template show");
302
328
  const idOrSlug = args.positionals[1];
303
329
  if (!idOrSlug) {
304
- fail("missing artifact <id|slug> — usage: pane artifact show <id|slug>", "invalid_args");
330
+ fail("missing template <id|slug> — usage: pane template show <id|slug>", "invalid_args");
305
331
  }
306
332
  const client = makeClient(args);
307
333
  try {
@@ -312,23 +338,24 @@ async function runArtifactShow(args) {
312
338
  failFromError(e);
313
339
  }
314
340
  }
315
- // `pane artifact delete <id|slug> --yes` — remove an artifact (and, server-
341
+ // `pane template delete <id|slug> --yes` — remove an template (and, server-
316
342
  // side, all its versions). The relay refuses with 409 conflict if any
317
- // session still references it; the CLI surfaces that as the relay-supplied
343
+ // surface still references it; the CLI surfaces that as the relay-supplied
318
344
  // envelope. `--yes` is required because there's no Undo button on a delete
319
- // and the same `pane artifact create` slug isn't reservable once gone.
345
+ // and the same `pane template create` slug isn't reservable once gone.
320
346
  async function runArtifactDelete(args) {
347
+ assertKnownFlags(args, NO_FLAGS, DELETE_BOOLS, "pane template delete");
321
348
  const idOrSlug = args.positionals[1];
322
349
  if (!idOrSlug) {
323
- fail("missing artifact <id|slug> — usage: pane artifact delete <id|slug> --yes", "invalid_args");
350
+ fail("missing template <id|slug> — usage: pane template delete <id|slug> --yes", "invalid_args");
324
351
  }
325
352
  if (!args.bools.has("yes")) {
326
- fail("'pane artifact delete' permanently removes the artifact and all its versions — it is destructive. Pass --yes to confirm.", "invalid_args");
353
+ fail("'pane template delete' permanently removes the template and all its versions — it is destructive. Pass --yes to confirm.", "invalid_args");
327
354
  }
328
355
  const client = makeClient(args);
329
356
  try {
330
357
  await client.deleteArtifact(idOrSlug);
331
- printJson({ artifact: idOrSlug, deleted: true });
358
+ printJson({ template: idOrSlug, deleted: true });
332
359
  }
333
360
  catch (e) {
334
361
  failFromError(e);
@@ -359,9 +386,9 @@ export async function runArtifact(args) {
359
386
  await runArtifactDelete(args);
360
387
  break;
361
388
  case undefined:
362
- fail("missing subcommand — usage: pane artifact <create|version|update|search|list|show|delete> (run 'pane artifact --help')", "invalid_args");
389
+ fail("missing subcommand — usage: pane template <create|version|update|search|list|show|delete> (run 'pane template --help')", "invalid_args");
363
390
  break;
364
391
  default:
365
- fail(`unknown artifact subcommand '${sub}' — expected create|version|update|search|list|show|delete (run 'pane artifact --help')`, "invalid_args");
392
+ fail(`unknown template subcommand '${sub}' — expected create|version|update|search|list|show|delete (run 'pane template --help')`, "invalid_args");
366
393
  }
367
394
  }