@paneui/cli 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/dist/commands/agent.js +17 -4
- package/dist/commands/attachment-delete.js +37 -0
- package/dist/commands/{blob-download.js → attachment-download.js} +15 -11
- package/dist/commands/{blob-list.js → attachment-list.js} +9 -9
- package/dist/commands/{blob-show.js → attachment-show.js} +11 -11
- package/dist/commands/{blob-token.js → attachment-token.js} +31 -31
- package/dist/commands/{blob-upload.js → attachment-upload.js} +20 -20
- package/dist/commands/{blob.js → attachment.js} +40 -40
- package/dist/commands/claim.js +68 -0
- package/dist/commands/create.js +71 -71
- package/dist/commands/delete.js +12 -12
- package/dist/commands/feedback.js +5 -5
- package/dist/commands/list.js +23 -23
- package/dist/commands/participant.js +38 -38
- package/dist/commands/send.js +33 -27
- package/dist/commands/state.js +12 -12
- package/dist/commands/{session.js → surface.js} +24 -24
- package/dist/commands/taste.js +14 -14
- package/dist/commands/{artifact.js → template.js} +76 -76
- package/dist/commands/watch.js +22 -22
- package/dist/index.js +17 -17
- package/dist/input.js +2 -2
- package/dist/output.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/commands/blob-delete.js +0 -37
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// `pane
|
|
2
|
-
// participant URL on an existing
|
|
3
|
-
// primitives that together replace the destructive `pane
|
|
4
|
-
// pane
|
|
1
|
+
// `pane surface participant <new|revoke>` — mint or invalidate one
|
|
2
|
+
// participant URL on an existing surface. Recovery + leak-containment
|
|
3
|
+
// primitives that together replace the destructive `pane surface delete +
|
|
4
|
+
// pane surface create` workaround for the lost-URL case.
|
|
5
5
|
//
|
|
6
|
-
// This file is a sub-noun dispatcher under `pane
|
|
6
|
+
// This file is a sub-noun dispatcher under `pane surface`. The surface
|
|
7
7
|
// dispatcher hands us a ParsedArgs whose positionals[0] is "participant"
|
|
8
8
|
// (our sub-noun marker), so we read the verb from positionals[1] and the
|
|
9
9
|
// args from positionals[2..]. This mirrors the way every other sub-verb
|
|
@@ -14,32 +14,32 @@ import { makeClient } from "../config.js";
|
|
|
14
14
|
import { printJson, fail, failFromError } from "../output.js";
|
|
15
15
|
const NO_FLAGS = [];
|
|
16
16
|
const NO_BOOLS = [];
|
|
17
|
-
export const participantHelp = `pane
|
|
17
|
+
export const participantHelp = `pane surface participant — manage one surface's participant URLs
|
|
18
18
|
|
|
19
19
|
Participant tokens are stored hashed on the relay and CANNOT be recovered.
|
|
20
20
|
If you lost the create-response (and the URL with it), use 'new' to mint a
|
|
21
|
-
fresh URL — the
|
|
22
|
-
Use 'revoke' to invalidate a single URL while keeping the
|
|
21
|
+
fresh URL — the surface keeps its event log, template pin, and created_at.
|
|
22
|
+
Use 'revoke' to invalidate a single URL while keeping the surface alive.
|
|
23
23
|
|
|
24
24
|
Usage:
|
|
25
|
-
pane
|
|
25
|
+
pane surface participant <verb> <args>
|
|
26
26
|
|
|
27
27
|
Verbs:
|
|
28
|
-
list <
|
|
28
|
+
list <surface-id> List the participants on one surface, including
|
|
29
29
|
revoked rows (for audit). Returns
|
|
30
|
-
{
|
|
30
|
+
{ surface_id, items: [...] } where each item
|
|
31
31
|
carries { participant_id, kind, token_prefix,
|
|
32
32
|
joined_at, revoked_at }. Use this to find the
|
|
33
33
|
participant_id you need to pass to 'revoke'.
|
|
34
34
|
|
|
35
|
-
new <
|
|
35
|
+
new <surface-id> Mint a fresh human URL on an existing surface.
|
|
36
36
|
Returns { participant_id, kind, token, url,
|
|
37
37
|
created_at } — ONCE. The plaintext token is
|
|
38
38
|
never recoverable; save the response (pipe to
|
|
39
39
|
a JSONL log) before delivering the URL.
|
|
40
40
|
|
|
41
|
-
revoke <
|
|
42
|
-
Invalidate one participant URL. The
|
|
41
|
+
revoke <surface-id> <participant-id>
|
|
42
|
+
Invalidate one participant URL. The surface's
|
|
43
43
|
other participants (and the agent's own
|
|
44
44
|
websocket) are untouched. Idempotent: running
|
|
45
45
|
revoke twice still returns success.
|
|
@@ -54,24 +54,24 @@ Options:
|
|
|
54
54
|
-h, --help Show this help.
|
|
55
55
|
|
|
56
56
|
Recovery recipe:
|
|
57
|
-
pane
|
|
58
|
-
pane
|
|
57
|
+
pane surface list # find surface_id
|
|
58
|
+
pane surface participant list <surface-id> # find participant
|
|
59
59
|
# ids on that
|
|
60
|
-
#
|
|
61
|
-
pane
|
|
62
|
-
pane
|
|
60
|
+
# surface
|
|
61
|
+
pane surface participant new <surface-id> # mint a new URL
|
|
62
|
+
pane surface participant revoke <surface-id> <p-id> # invalidate the
|
|
63
63
|
# old URL
|
|
64
64
|
|
|
65
65
|
Output: stdout is machine-readable JSON.`;
|
|
66
66
|
async function runParticipantList(args) {
|
|
67
|
-
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane
|
|
68
|
-
const
|
|
69
|
-
if (!
|
|
70
|
-
fail("missing <
|
|
67
|
+
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane surface participant list");
|
|
68
|
+
const surfaceId = args.positionals[1];
|
|
69
|
+
if (!surfaceId) {
|
|
70
|
+
fail("missing <surface-id> — usage: pane surface participant list <surface-id>", "invalid_args");
|
|
71
71
|
}
|
|
72
72
|
const client = makeClient(args);
|
|
73
73
|
try {
|
|
74
|
-
const res = await client.listParticipants(
|
|
74
|
+
const res = await client.listParticipants(surfaceId);
|
|
75
75
|
printJson(res);
|
|
76
76
|
}
|
|
77
77
|
catch (e) {
|
|
@@ -79,14 +79,14 @@ async function runParticipantList(args) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
async function runParticipantNew(args) {
|
|
82
|
-
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane
|
|
83
|
-
const
|
|
84
|
-
if (!
|
|
85
|
-
fail("missing <
|
|
82
|
+
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane surface participant new");
|
|
83
|
+
const surfaceId = args.positionals[1];
|
|
84
|
+
if (!surfaceId) {
|
|
85
|
+
fail("missing <surface-id> — usage: pane surface participant new <surface-id>", "invalid_args");
|
|
86
86
|
}
|
|
87
87
|
const client = makeClient(args);
|
|
88
88
|
try {
|
|
89
|
-
const res = await client.mintParticipant(
|
|
89
|
+
const res = await client.mintParticipant(surfaceId);
|
|
90
90
|
printJson(res);
|
|
91
91
|
}
|
|
92
92
|
catch (e) {
|
|
@@ -94,17 +94,17 @@ async function runParticipantNew(args) {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
async function runParticipantRevoke(args) {
|
|
97
|
-
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane
|
|
98
|
-
const
|
|
97
|
+
assertKnownFlags(args, NO_FLAGS, NO_BOOLS, "pane surface participant revoke");
|
|
98
|
+
const surfaceId = args.positionals[1];
|
|
99
99
|
const participantId = args.positionals[2];
|
|
100
|
-
if (!
|
|
101
|
-
fail("missing arguments — usage: pane
|
|
100
|
+
if (!surfaceId || !participantId) {
|
|
101
|
+
fail("missing arguments — usage: pane surface participant revoke <surface-id> <participant-id>", "invalid_args");
|
|
102
102
|
}
|
|
103
103
|
const client = makeClient(args);
|
|
104
104
|
try {
|
|
105
|
-
await client.revokeParticipant(
|
|
105
|
+
await client.revokeParticipant(surfaceId, participantId);
|
|
106
106
|
printJson({
|
|
107
|
-
|
|
107
|
+
surface_id: surfaceId,
|
|
108
108
|
participant_id: participantId,
|
|
109
109
|
revoked: true,
|
|
110
110
|
});
|
|
@@ -115,7 +115,7 @@ async function runParticipantRevoke(args) {
|
|
|
115
115
|
}
|
|
116
116
|
export async function runParticipant(args) {
|
|
117
117
|
// positionals[0] is the verb (list | new | revoke), positionals[1..] are
|
|
118
|
-
// the verb's args. (The
|
|
118
|
+
// the verb's args. (The surface.ts dispatcher already shifted off the
|
|
119
119
|
// "participant" marker before calling us.)
|
|
120
120
|
const verb = args.positionals[0];
|
|
121
121
|
switch (verb) {
|
|
@@ -129,9 +129,9 @@ export async function runParticipant(args) {
|
|
|
129
129
|
await runParticipantRevoke(args);
|
|
130
130
|
break;
|
|
131
131
|
case undefined:
|
|
132
|
-
fail("missing verb — usage: pane
|
|
132
|
+
fail("missing verb — usage: pane surface participant <list|new|revoke> (run 'pane surface participant --help')", "invalid_args");
|
|
133
133
|
break;
|
|
134
134
|
default:
|
|
135
|
-
fail(`unknown participant verb '${verb}' — expected list|new|revoke (run 'pane
|
|
135
|
+
fail(`unknown participant verb '${verb}' — expected list|new|revoke (run 'pane surface participant --help')`, "invalid_args");
|
|
136
136
|
}
|
|
137
137
|
}
|
package/dist/commands/send.js
CHANGED
|
@@ -1,32 +1,38 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane surface send <id>` — append an agent event to a surface.
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
4
|
import { assertKnownFlags } from "../argv.js";
|
|
5
5
|
import { makeClient } from "../config.js";
|
|
6
6
|
import { resolveJson } from "../input.js";
|
|
7
7
|
import { printJson, fail, failFromError } from "../output.js";
|
|
8
|
-
const KNOWN_FLAGS = [
|
|
8
|
+
const KNOWN_FLAGS = [
|
|
9
|
+
"type",
|
|
10
|
+
"data",
|
|
11
|
+
"attachment",
|
|
12
|
+
"causation-id",
|
|
13
|
+
"idempotency-key",
|
|
14
|
+
];
|
|
9
15
|
const KNOWN_BOOLS = [];
|
|
10
|
-
export const sendHelp = `pane
|
|
16
|
+
export const sendHelp = `pane surface send — emit an agent event into a surface
|
|
11
17
|
|
|
12
18
|
Usage:
|
|
13
|
-
pane
|
|
14
|
-
pane
|
|
19
|
+
pane surface send <surface-id> --type <event-type> --data <path|json> [options]
|
|
20
|
+
pane surface send <surface-id> --type <event-type> --attachment <file-path> [options]
|
|
15
21
|
|
|
16
|
-
POSTs an event to /v1/
|
|
22
|
+
POSTs an event to /v1/surfaces/:id/events. The event is stamped as authored by
|
|
17
23
|
the agent (the relay derives identity from the API key — it cannot be spoofed).
|
|
18
24
|
|
|
19
25
|
Required:
|
|
20
|
-
--type <t> Event type. Must exist in the
|
|
26
|
+
--type <t> Event type. Must exist in the surface's event schema
|
|
21
27
|
with the agent in its emittedBy list.
|
|
22
28
|
--data <v> Event payload: a file path to a .json file, or inline
|
|
23
29
|
JSON. Use --data 'null' or --data '{}' for no payload.
|
|
24
30
|
|
|
25
31
|
ALTERNATIVE to --data:
|
|
26
|
-
--
|
|
27
|
-
send an event whose payload is the
|
|
28
|
-
data is {
|
|
29
|
-
schema with \`format: pane-
|
|
32
|
+
--attachment <path> One-shot: upload <path> as a surface-scope attachment, then
|
|
33
|
+
send an event whose payload is the AttachmentRef. The event
|
|
34
|
+
data is { attachment: <AttachmentRef> }; declare it in your event
|
|
35
|
+
schema with \`format: pane-attachment-id\` on \`attachment.attachment_id\`.
|
|
30
36
|
|
|
31
37
|
Options:
|
|
32
38
|
--causation-id <id> Opaque causation id stored verbatim on the event.
|
|
@@ -38,42 +44,42 @@ Options:
|
|
|
38
44
|
Output (stdout, JSON):
|
|
39
45
|
{ event, deduped }`;
|
|
40
46
|
export async function runSend(args) {
|
|
41
|
-
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane
|
|
42
|
-
const
|
|
43
|
-
if (!
|
|
44
|
-
fail("missing <
|
|
47
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane surface send");
|
|
48
|
+
const surfaceId = args.positionals[0];
|
|
49
|
+
if (!surfaceId)
|
|
50
|
+
fail("missing <surface-id>", "invalid_args");
|
|
45
51
|
const type = args.flags.get("type");
|
|
46
52
|
if (!type)
|
|
47
53
|
fail("missing --type", "invalid_args");
|
|
48
54
|
const dataRaw = args.flags.get("data");
|
|
49
|
-
const blobPath = args.flags.get("
|
|
55
|
+
const blobPath = args.flags.get("attachment");
|
|
50
56
|
if (dataRaw !== undefined && blobPath !== undefined) {
|
|
51
|
-
fail("--data and --
|
|
57
|
+
fail("--data and --attachment are mutually exclusive", "invalid_args");
|
|
52
58
|
}
|
|
53
59
|
if (dataRaw === undefined && blobPath === undefined) {
|
|
54
|
-
fail("missing --data or --
|
|
60
|
+
fail("missing --data or --attachment", "invalid_args");
|
|
55
61
|
}
|
|
56
62
|
const client = makeClient(args);
|
|
57
|
-
// --
|
|
58
|
-
// event whose data is {
|
|
59
|
-
// is expected to declare a
|
|
63
|
+
// --attachment path: upload the file as a surface-scope attachment, then send an
|
|
64
|
+
// event whose data is { attachment: <AttachmentRef> }. The surface's event schema
|
|
65
|
+
// is expected to declare a attachment field with format: pane-attachment-id.
|
|
60
66
|
if (blobPath !== undefined) {
|
|
61
67
|
let bytes;
|
|
62
68
|
try {
|
|
63
69
|
bytes = readFileSync(blobPath);
|
|
64
70
|
}
|
|
65
71
|
catch (e) {
|
|
66
|
-
fail(`failed to read --
|
|
72
|
+
fail(`failed to read --attachment '${blobPath}': ${e instanceof Error ? e.message : String(e)}`, "invalid_args");
|
|
67
73
|
}
|
|
68
74
|
try {
|
|
69
75
|
const ref = await client.uploadBlob(bytes, {
|
|
70
|
-
scope: "
|
|
71
|
-
|
|
76
|
+
scope: "surface",
|
|
77
|
+
surfaceId: surfaceId,
|
|
72
78
|
filename: basename(blobPath),
|
|
73
79
|
});
|
|
74
|
-
const res = await client.sendEvent(
|
|
80
|
+
const res = await client.sendEvent(surfaceId, {
|
|
75
81
|
type: type,
|
|
76
|
-
data: {
|
|
82
|
+
data: { attachment: ref },
|
|
77
83
|
causationId: args.flags.get("causation-id"),
|
|
78
84
|
idempotencyKey: args.flags.get("idempotency-key"),
|
|
79
85
|
});
|
|
@@ -92,7 +98,7 @@ export async function runSend(args) {
|
|
|
92
98
|
fail(e instanceof Error ? e.message : String(e), "invalid_args");
|
|
93
99
|
}
|
|
94
100
|
try {
|
|
95
|
-
const res = await client.sendEvent(
|
|
101
|
+
const res = await client.sendEvent(surfaceId, {
|
|
96
102
|
type: type,
|
|
97
103
|
data,
|
|
98
104
|
causationId: args.flags.get("causation-id"),
|
package/dist/commands/state.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
// `pane
|
|
1
|
+
// `pane surface show <id>` — snapshot of a surface, optionally long-polled.
|
|
2
2
|
import { assertKnownFlags } from "../argv.js";
|
|
3
3
|
import { makeClient } from "../config.js";
|
|
4
4
|
import { printJson, fail, failFromError } from "../output.js";
|
|
5
5
|
const KNOWN_FLAGS = ["since", "wait"];
|
|
6
6
|
const KNOWN_BOOLS = [];
|
|
7
|
-
export const stateHelp = `pane
|
|
7
|
+
export const stateHelp = `pane surface show — show a surface's metadata and event log
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
|
-
pane
|
|
10
|
+
pane surface show <surface-id> [options]
|
|
11
11
|
|
|
12
|
-
By default non-blocking: fetches
|
|
13
|
-
the event log (GET /v1/
|
|
12
|
+
By default non-blocking: fetches surface metadata (GET /v1/surfaces/:id) plus
|
|
13
|
+
the event log (GET /v1/surfaces/:id/events) and prints them together.
|
|
14
14
|
|
|
15
15
|
With --wait, blocks at the relay for up to <secs> if no new events are
|
|
16
16
|
available since the cursor — returns as soon as something lands. Use this
|
|
17
17
|
for headless polling agents that can't keep a WebSocket open (cron,
|
|
18
18
|
FaaS, slow links): poll, then re-poll using next_cursor as --since on the
|
|
19
|
-
next call. Compared to 'pane
|
|
19
|
+
next call. Compared to 'pane surface watch', it's higher latency per
|
|
20
20
|
round-trip but no long-lived connection.
|
|
21
21
|
|
|
22
22
|
Options:
|
|
@@ -34,10 +34,10 @@ Options:
|
|
|
34
34
|
Output (stdout, JSON):
|
|
35
35
|
{ meta, events, next_cursor }`;
|
|
36
36
|
export async function runState(args) {
|
|
37
|
-
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane
|
|
38
|
-
const
|
|
39
|
-
if (!
|
|
40
|
-
fail("missing <
|
|
37
|
+
assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane surface show");
|
|
38
|
+
const surfaceId = args.positionals[0];
|
|
39
|
+
if (!surfaceId)
|
|
40
|
+
fail("missing <surface-id>", "invalid_args");
|
|
41
41
|
const since = args.flags.get("since") ?? null;
|
|
42
42
|
// --wait <secs>: hand the server the long-poll window. The relay caps
|
|
43
43
|
// this at 30s; we pass the raw value and let the relay clamp (sending
|
|
@@ -54,8 +54,8 @@ export async function runState(args) {
|
|
|
54
54
|
}
|
|
55
55
|
const client = makeClient(args);
|
|
56
56
|
try {
|
|
57
|
-
const meta = await client.getSession(
|
|
58
|
-
const page = await client.getEvents(
|
|
57
|
+
const meta = await client.getSession(surfaceId);
|
|
58
|
+
const page = await client.getEvents(surfaceId, {
|
|
59
59
|
since,
|
|
60
60
|
...(waitSeconds !== undefined ? { waitSeconds } : {}),
|
|
61
61
|
});
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
// `pane
|
|
2
|
-
// close a
|
|
1
|
+
// `pane surface` — the central noun of pane: open, observe, send to, and
|
|
2
|
+
// close a surface.
|
|
3
3
|
//
|
|
4
|
-
// A
|
|
4
|
+
// A surface is one *use* of an template: an open URL the human(s) interact
|
|
5
5
|
// with, plus an event log the agent reads and appends to. Every other noun
|
|
6
|
-
// (
|
|
6
|
+
// (template, attachment, key, taste, feedback) exists in service of surfaces.
|
|
7
7
|
//
|
|
8
8
|
// This file is a thin dispatcher — each verb's actual logic lives in its own
|
|
9
9
|
// file (create.ts, state.ts, send.ts, watch.ts, delete.ts). The verb runners
|
|
10
|
-
// expect the
|
|
11
|
-
// delegating so they don't need to know they're being called via `
|
|
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
12
|
import { runCreate } from "./create.js";
|
|
13
13
|
import { runState } from "./state.js";
|
|
14
14
|
import { runSend } from "./send.js";
|
|
@@ -17,39 +17,39 @@ import { runDelete } from "./delete.js";
|
|
|
17
17
|
import { runList, listHelp } from "./list.js";
|
|
18
18
|
import { runParticipant, participantHelp } from "./participant.js";
|
|
19
19
|
import { fail } from "../output.js";
|
|
20
|
-
export const sessionHelp = `pane
|
|
20
|
+
export const sessionHelp = `pane surface — open, observe, send to, and close surfaces
|
|
21
21
|
|
|
22
|
-
A
|
|
22
|
+
A surface is one use of an template: an open URL the human(s) interact with,
|
|
23
23
|
plus an event log the agent reads and appends to.
|
|
24
24
|
|
|
25
25
|
Usage:
|
|
26
|
-
pane
|
|
26
|
+
pane surface <verb> [options]
|
|
27
27
|
|
|
28
28
|
Verbs:
|
|
29
|
-
create Create a
|
|
29
|
+
create Create a surface (POST /v1/surfaces). Prints surface_id,
|
|
30
30
|
urls, tokens, expires_at.
|
|
31
|
-
list Enumerate YOUR agent's
|
|
32
|
-
for "I dropped the create response" —
|
|
31
|
+
list Enumerate YOUR agent's surfaces. The recovery primitive
|
|
32
|
+
for "I dropped the create response" — surfaces are
|
|
33
33
|
listable, but participant tokens are stored hashed and
|
|
34
34
|
CANNOT be recovered. Use 'participant new' to mint a
|
|
35
35
|
fresh URL.
|
|
36
|
-
show <id> Non-blocking snapshot:
|
|
36
|
+
show <id> Non-blocking snapshot: surface metadata + event log.
|
|
37
37
|
Supports --wait <secs> for relay-side long-polling.
|
|
38
|
-
send <id> Emit an agent event into a
|
|
39
|
-
watch <id> Stream a
|
|
38
|
+
send <id> Emit an agent event into a surface.
|
|
39
|
+
watch <id> Stream a surface's events as JSON-lines on stdout
|
|
40
40
|
(long-lived; the building block for pipe-readers).
|
|
41
|
-
delete <id> Close/delete a
|
|
41
|
+
delete <id> Close/delete a surface (DELETE /v1/surfaces/:id).
|
|
42
42
|
participant List / mint / revoke participant URLs on an existing
|
|
43
|
-
<list|new|revoke>
|
|
43
|
+
<list|new|revoke> surface. 'list' returns the participant ids you need
|
|
44
44
|
for 'revoke'; 'new' replaces the destructive 'delete
|
|
45
45
|
+ recreate' workaround for a lost URL; 'revoke'
|
|
46
|
-
invalidates one URL without touching the
|
|
46
|
+
invalidates one URL without touching the surface.
|
|
47
47
|
|
|
48
|
-
Run \`pane
|
|
48
|
+
Run \`pane surface <verb> --help\` for verb-specific options.`;
|
|
49
49
|
/**
|
|
50
50
|
* Build a new ParsedArgs with the leading positional (the verb) stripped.
|
|
51
51
|
* The downstream verb runners (runState / runSend / runWatch / runDelete)
|
|
52
|
-
* read the
|
|
52
|
+
* read the surface id at positionals[0], so we hand them an args object that
|
|
53
53
|
* looks exactly like the pre-restructure invocation.
|
|
54
54
|
*/
|
|
55
55
|
function shiftPositionals(args) {
|
|
@@ -68,7 +68,7 @@ function shiftPositionals(args) {
|
|
|
68
68
|
}
|
|
69
69
|
export async function runSession(args) {
|
|
70
70
|
const verb = args.positionals[0];
|
|
71
|
-
// `pane
|
|
71
|
+
// `pane surface participant --help` (verb-level help on the participant
|
|
72
72
|
// sub-noun, with no further sub-verb). The general --help pre-empt in
|
|
73
73
|
// index.ts only fires when no positional follows the noun; here a
|
|
74
74
|
// positional ("participant") is present, so the sub-noun must own its own
|
|
@@ -79,7 +79,7 @@ export async function runSession(args) {
|
|
|
79
79
|
process.stdout.write(participantHelp + "\n");
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
|
-
// `pane
|
|
82
|
+
// `pane surface list --help` — same pattern.
|
|
83
83
|
if (verb === "list" &&
|
|
84
84
|
args.bools.has("help") &&
|
|
85
85
|
args.positionals.length === 1) {
|
|
@@ -110,9 +110,9 @@ export async function runSession(args) {
|
|
|
110
110
|
await runParticipant(inner);
|
|
111
111
|
break;
|
|
112
112
|
case undefined:
|
|
113
|
-
fail("missing verb — usage: pane
|
|
113
|
+
fail("missing verb — usage: pane surface <create|list|show|send|watch|delete|participant> (run 'pane surface --help')", "invalid_args");
|
|
114
114
|
break;
|
|
115
115
|
default:
|
|
116
|
-
fail(`unknown
|
|
116
|
+
fail(`unknown surface verb '${verb}' — expected create|list|show|send|watch|delete|participant (run 'pane surface --help')`, "invalid_args");
|
|
117
117
|
}
|
|
118
118
|
}
|
package/dist/commands/taste.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
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
21
|
import { assertKnownFlags } from "../argv.js";
|
|
@@ -27,25 +27,25 @@ const SET_FLAGS = ["file"];
|
|
|
27
27
|
const CLEAR_BOOLS = ["yes"];
|
|
28
28
|
export const tasteHelp = `pane taste — read / write / clear YOUR agent's UI taste notes
|
|
29
29
|
|
|
30
|
-
Taste notes are a small markdown
|
|
30
|
+
Taste notes are a small markdown attachment storing presentation preferences your
|
|
31
31
|
agent has picked up from human feedback ("denser table", "no rounded corners",
|
|
32
|
-
"use a dark header"). Read them before generating a pane
|
|
32
|
+
"use a dark header"). Read them before generating a pane template so prior
|
|
33
33
|
feedback shapes the output; rewrite them whenever the human gives new
|
|
34
34
|
presentation feedback. Keep entries about UI/presentation taste only — not
|
|
35
|
-
project context, todos, or
|
|
35
|
+
project context, todos, or surface state.
|
|
36
36
|
|
|
37
37
|
Usage:
|
|
38
38
|
pane taste <subcommand> [options]
|
|
39
39
|
|
|
40
40
|
Subcommands:
|
|
41
|
-
get Print the current notes
|
|
41
|
+
get Print the current notes attachment:
|
|
42
42
|
{ taste: string|null, updated_at: string|null, bytes: number }.
|
|
43
43
|
taste is null and bytes is 0 when notes have never been written.
|
|
44
44
|
|
|
45
|
-
set Whole-
|
|
45
|
+
set Whole-attachment replace. Source the markdown via --file <path>,
|
|
46
46
|
--file - (read stdin), or by piping into 'pane taste set' with
|
|
47
47
|
no flag. The relay rejects empty/whitespace-only payloads and
|
|
48
|
-
caps the
|
|
48
|
+
caps the attachment at MAX_TASTE_BYTES (utf8). To clear the notes,
|
|
49
49
|
use 'pane taste clear', not 'set' with an empty body.
|
|
50
50
|
|
|
51
51
|
clear Delete the notes. Requires --yes (it is destructive). Prints
|
|
@@ -92,7 +92,7 @@ async function runTasteGet(args) {
|
|
|
92
92
|
async function runTasteSet(args) {
|
|
93
93
|
assertKnownFlags(args, SET_FLAGS, NO_BOOLS, "pane taste set");
|
|
94
94
|
const filePath = args.flags.get("file");
|
|
95
|
-
// Source the
|
|
95
|
+
// Source the attachment deterministically — no isTTY-flag fusing, because
|
|
96
96
|
// `!process.stdin.isTTY` is true under every non-interactive caller
|
|
97
97
|
// (pipes, redirects, closed fd, CI, agent harnesses) and would wrongly
|
|
98
98
|
// reject `--file` for the entire target audience. See issue #148.
|
|
@@ -119,7 +119,7 @@ async function runTasteSet(args) {
|
|
|
119
119
|
fail("'pane taste set' needs input — pass --file <path>, pipe markdown on stdin, or use --file -", "invalid_args");
|
|
120
120
|
}
|
|
121
121
|
if (taste.trim().length === 0) {
|
|
122
|
-
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");
|
|
123
123
|
}
|
|
124
124
|
const client = makeClient(args);
|
|
125
125
|
try {
|