@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,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
|
+
}
|
package/dist/commands/taste.js
CHANGED
|
@@ -1,46 +1,51 @@
|
|
|
1
1
|
// `pane taste` — read / write / clear the calling agent's freeform "taste
|
|
2
|
-
// notes" markdown
|
|
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
|
|
6
|
+
// kind of guidance that should outlive a single surface. The intended loop:
|
|
7
7
|
//
|
|
8
|
-
// 1. Before generating a pane
|
|
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
|
|
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-
|
|
18
|
-
// somewhere else. Today the
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1
|
+
// `pane template` — manage reusable, versioned templates.
|
|
2
2
|
//
|
|
3
|
-
// Flat command namespace: `
|
|
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
|
|
7
|
-
// schema); a
|
|
8
|
-
// once and instancing it via `pane create --
|
|
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
|
-
|
|
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
|
|
17
|
-
input schema. A
|
|
18
|
-
once, then instance it many times with 'pane create --
|
|
19
|
-
instead of regenerating the HTML on every
|
|
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
|
|
43
|
+
pane template <subcommand> [options]
|
|
23
44
|
|
|
24
45
|
Subcommands:
|
|
25
|
-
create Create a named, reusable
|
|
26
|
-
version Append a new version to an existing
|
|
27
|
-
update Update an
|
|
28
|
-
search Search the agent's named
|
|
29
|
-
list List the agent's named
|
|
30
|
-
show Show a full
|
|
31
|
-
delete Permanently delete an
|
|
32
|
-
--yes. Refused with 409 conflict if any
|
|
33
|
-
closed) still references the
|
|
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
|
|
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>] [--
|
|
39
|
-
Creates a named
|
|
59
|
+
[--input-schema <path|json>] [--template-type <t>]
|
|
60
|
+
Creates a named template. Prints { template_id, slug, version }.
|
|
40
61
|
|
|
41
|
-
pane
|
|
62
|
+
pane template version <id|slug> --template <path|inline>
|
|
42
63
|
[--event-schema <path|json>]
|
|
43
|
-
[--input-schema <path|json>] [--
|
|
44
|
-
Appends a new immutable version. Prints {
|
|
64
|
+
[--input-schema <path|json>] [--template-type <t>]
|
|
65
|
+
Appends a new immutable version. Prints { template_id, version }.
|
|
45
66
|
|
|
46
|
-
pane
|
|
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
|
|
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
|
|
56
|
-
Alias of 'search' with no query — lists all the agent's
|
|
76
|
+
pane template list
|
|
77
|
+
Alias of 'search' with no query — lists all the agent's templates.
|
|
57
78
|
|
|
58
|
-
pane
|
|
59
|
-
Prints the full
|
|
79
|
+
pane template show <id|slug>
|
|
80
|
+
Prints the full template: head metadata + every version's content.
|
|
60
81
|
|
|
61
|
-
pane
|
|
62
|
-
Permanently deletes the
|
|
63
|
-
(409 conflict) if any
|
|
64
|
-
of the
|
|
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 {
|
|
87
|
+
Prints { template, deleted: true } on success.
|
|
67
88
|
|
|
68
89
|
Options:
|
|
69
|
-
--name <n>
|
|
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
|
|
72
|
-
--description <d> Prose: what the
|
|
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
|
-
--
|
|
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
|
|
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
|
|
120
|
+
--input-schema <v> JSON Schema for this template's per-surface input_data —
|
|
100
121
|
a file path, or inline JSON. Optional.
|
|
101
|
-
--
|
|
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 --
|
|
128
|
+
/** Resolve --template-type, defaulting to html-inline. */
|
|
108
129
|
function resolveArtifactType(args) {
|
|
109
|
-
const t = (args.flags.get("
|
|
130
|
+
const t = (args.flags.get("template-type") ?? "html-inline");
|
|
110
131
|
if (t !== "html-inline" && t !== "html-ref") {
|
|
111
|
-
fail("--
|
|
132
|
+
fail("--template-type must be 'html-inline' or 'html-ref'", "invalid_args");
|
|
112
133
|
}
|
|
113
134
|
return t;
|
|
114
135
|
}
|
|
115
|
-
/** Resolve the
|
|
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("
|
|
138
|
+
const artifactVal = args.flags.get("template");
|
|
118
139
|
if (!artifactVal)
|
|
119
|
-
fail("missing --
|
|
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
|
-
*
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
|
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({
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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({
|
|
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
|
|
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
|
|
392
|
+
fail(`unknown template subcommand '${sub}' — expected create|version|update|search|list|show|delete (run 'pane template --help')`, "invalid_args");
|
|
366
393
|
}
|
|
367
394
|
}
|