@paneui/cli 0.0.9 → 0.0.11
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 +8 -8
- package/dist/argv.js +3 -3
- package/dist/commands/agent.js +10 -2
- package/dist/commands/attachment-token.js +2 -2
- package/dist/commands/attachment-upload.js +8 -10
- package/dist/commands/attachment.js +7 -7
- package/dist/commands/claim.js +1 -1
- package/dist/commands/config.js +232 -20
- package/dist/commands/create.js +132 -21
- package/dist/commands/delete.js +12 -12
- package/dist/commands/feedback.js +5 -5
- package/dist/commands/list.js +17 -17
- package/dist/commands/logout.js +43 -13
- package/dist/commands/participant.js +38 -38
- package/dist/commands/query.js +204 -0
- package/dist/commands/records.js +285 -0
- package/dist/commands/register.js +53 -15
- package/dist/commands/send.js +17 -17
- package/dist/commands/set-key.js +92 -0
- package/dist/commands/skill.js +1 -1
- package/dist/commands/state.js +12 -12
- package/dist/commands/taste.js +3 -3
- package/dist/commands/template-records.js +195 -0
- package/dist/commands/template.js +243 -35
- package/dist/commands/trash.js +102 -0
- package/dist/commands/watch.js +22 -22
- package/dist/config.js +87 -20
- package/dist/format.js +133 -0
- package/dist/index.js +97 -20
- package/dist/output.js +1 -1
- package/dist/store.js +167 -26
- package/dist/upgrade.js +1 -1
- package/dist/version.js +2 -2
- package/package.json +5 -3
- package/dist/commands/surface.js +0 -118
package/README.md
CHANGED
|
@@ -34,11 +34,11 @@ Uniform `pane <noun> <verb> [options]`:
|
|
|
34
34
|
```
|
|
35
35
|
pane agent register Provision an agent API key and save it locally
|
|
36
36
|
pane agent logout Clear the locally-saved URL + API key
|
|
37
|
-
pane
|
|
38
|
-
pane
|
|
39
|
-
pane
|
|
40
|
-
pane
|
|
41
|
-
pane
|
|
37
|
+
pane create Create a pane — returns pane_id, urls, tokens
|
|
38
|
+
pane show <id> Non-blocking snapshot: metadata + event log
|
|
39
|
+
pane send <id> Emit an agent event into a pane
|
|
40
|
+
pane watch <id> Stream a pane's events as JSON-lines on stdout
|
|
41
|
+
pane delete <id> Close / delete a pane
|
|
42
42
|
pane template <verb> Manage reusable, versioned templates
|
|
43
43
|
pane key list | revoke Inspect or revoke your agent's API key
|
|
44
44
|
pane taste get | set | clear Read / write / clear UI-taste notes
|
|
@@ -56,12 +56,12 @@ stdout is machine-readable JSON. Errors go to stderr as
|
|
|
56
56
|
`{"error":{"code","message"}}` with a non-zero exit.
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
|
-
SESSION=$(pane
|
|
60
|
-
pane
|
|
59
|
+
SESSION=$(pane create --template ./form.html --name "Quick poll" --event-schema ./q.json | jq -r .pane_id)
|
|
60
|
+
pane watch "$SESSION" | jq 'select(.type == "human_response")'
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
## Links
|
|
64
64
|
|
|
65
65
|
- Repo: <https://github.com/aerolalit/paneui>
|
|
66
|
-
- Spec: <https://github.com/aerolalit/paneui/
|
|
66
|
+
- Spec: <https://github.com/aerolalit/paneui/blob/main/docs/SPEC.md>
|
|
67
67
|
- License: MIT
|
package/dist/argv.js
CHANGED
|
@@ -89,7 +89,7 @@ export function parseArgs(tokens, booleanFlags) {
|
|
|
89
89
|
* so adding a new global flag updates one place. `url` / `api-key` are the
|
|
90
90
|
* relay-target overrides; `help` / `json` are universal display modes.
|
|
91
91
|
*/
|
|
92
|
-
const GLOBAL_FLAGS = ["url", "api-key"];
|
|
92
|
+
const GLOBAL_FLAGS = ["url", "api-key", "profile"];
|
|
93
93
|
const GLOBAL_BOOLS = ["help", "json"];
|
|
94
94
|
/**
|
|
95
95
|
* Reject anything the per-command allow-list (plus the globals above) does
|
|
@@ -104,7 +104,7 @@ const GLOBAL_BOOLS = ["help", "json"];
|
|
|
104
104
|
*
|
|
105
105
|
* Also resolves the parser's `danglingValueFlags`: an unknown name there
|
|
106
106
|
* is reported alongside other unknowns ("unknown flag(s): --bogus"); a
|
|
107
|
-
* known name there
|
|
107
|
+
* known name there panes as "--name requires a value". This is what
|
|
108
108
|
* keeps the error message uniform for a typo whether or not a value
|
|
109
109
|
* follows it.
|
|
110
110
|
*/
|
|
@@ -129,7 +129,7 @@ export function assertKnownFlags(args, knownFlags, knownBools, helpCommand) {
|
|
|
129
129
|
throw new ArgvError(`unknown flag(s): ${unknown.join(", ")}`, `run \`${helpCommand} --help\` for the supported flags`);
|
|
130
130
|
}
|
|
131
131
|
// No unknowns — but a known value-flag may still have been left without
|
|
132
|
-
// a value.
|
|
132
|
+
// a value. Pane the first such case with the pre-existing message
|
|
133
133
|
// shape ("--name requires a value"). Reporting only the first keeps the
|
|
134
134
|
// message simple; the user fixes that flag, re-runs, sees the next one.
|
|
135
135
|
for (const k of dangling) {
|
package/dist/commands/agent.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { runRegister } from "./register.js";
|
|
11
11
|
import { runLogout } from "./logout.js";
|
|
12
12
|
import { runClaim } from "./claim.js";
|
|
13
|
+
import { runSetKey } from "./set-key.js";
|
|
13
14
|
import { fail } from "../output.js";
|
|
14
15
|
export const agentHelp = `pane agent — manage this agent's identity on the relay
|
|
15
16
|
|
|
@@ -22,6 +23,10 @@ Verbs:
|
|
|
22
23
|
claim <code> Bind this agent to a human via a one-shot claim code the
|
|
23
24
|
human generated in their Settings UI (POST /v1/agents/claim).
|
|
24
25
|
One-way; no unclaim in v1.
|
|
26
|
+
set-key <key> Save a new API key into the CLI config file. Used after
|
|
27
|
+
regenerating the agent's key in the relay's My-agents UI:
|
|
28
|
+
the human pastes the new key here so subsequent commands
|
|
29
|
+
authenticate as the same agent.
|
|
25
30
|
logout Clear the locally-saved relay URL + API key. Does NOT
|
|
26
31
|
revoke the key on the relay — use 'pane key revoke' for
|
|
27
32
|
that.
|
|
@@ -42,13 +47,16 @@ export async function runAgent(args) {
|
|
|
42
47
|
case "claim":
|
|
43
48
|
await runClaim(verbArgs);
|
|
44
49
|
break;
|
|
50
|
+
case "set-key":
|
|
51
|
+
await runSetKey(verbArgs);
|
|
52
|
+
break;
|
|
45
53
|
case "logout":
|
|
46
54
|
await runLogout(verbArgs);
|
|
47
55
|
break;
|
|
48
56
|
case undefined:
|
|
49
|
-
fail("missing verb — usage: pane agent <register|claim|logout> (run 'pane agent --help')", "invalid_args");
|
|
57
|
+
fail("missing verb — usage: pane agent <register|claim|set-key|logout> (run 'pane agent --help')", "invalid_args");
|
|
50
58
|
break;
|
|
51
59
|
default:
|
|
52
|
-
fail(`unknown agent verb '${verb}' — expected register|claim|logout (run 'pane agent --help')`, "invalid_args");
|
|
60
|
+
fail(`unknown agent verb '${verb}' — expected register|claim|set-key|logout (run 'pane agent --help')`, "invalid_args");
|
|
53
61
|
}
|
|
54
62
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// hands us a ParsedArgs whose positionals[0] is "token" (our sub-noun
|
|
11
11
|
// marker), so we read the verb from positionals[1] and the args from
|
|
12
12
|
// positionals[2..]. Mirrors how participant.ts dispatches under `pane
|
|
13
|
-
//
|
|
13
|
+
// pane participant`.
|
|
14
14
|
import { assertKnownFlags } from "../argv.js";
|
|
15
15
|
import { makeClient } from "../config.js";
|
|
16
16
|
import { fail, failFromError, printJson } from "../output.js";
|
|
@@ -31,7 +31,7 @@ Usage:
|
|
|
31
31
|
Verbs:
|
|
32
32
|
mint <attachment-id> Mint a /b/<token> capability URL for one attachment.
|
|
33
33
|
Optional: --ttl <seconds> (defaults by scope:
|
|
34
|
-
30d template /
|
|
34
|
+
30d template / pane TTL / 24h agent; the caller
|
|
35
35
|
can only shorten), --once (token self-deletes on
|
|
36
36
|
first successful GET). Returns { token, url,
|
|
37
37
|
expires_at, ... } — ONCE.
|
|
@@ -7,7 +7,7 @@ import { fail, failFromError, printJson } from "../output.js";
|
|
|
7
7
|
const KNOWN_FLAGS = [
|
|
8
8
|
"file",
|
|
9
9
|
"scope",
|
|
10
|
-
"
|
|
10
|
+
"pane-id",
|
|
11
11
|
"template-id",
|
|
12
12
|
"filename",
|
|
13
13
|
"mime",
|
|
@@ -22,8 +22,8 @@ Required:
|
|
|
22
22
|
--file <path> Local file to upload.
|
|
23
23
|
|
|
24
24
|
Scope (default: agent):
|
|
25
|
-
--scope <s> "agent" | "
|
|
26
|
-
--
|
|
25
|
+
--scope <s> "agent" | "pane" | "template".
|
|
26
|
+
--pane-id <id> Required when --scope=pane.
|
|
27
27
|
--template-id <id> Required when --scope=template.
|
|
28
28
|
|
|
29
29
|
Optional:
|
|
@@ -50,14 +50,12 @@ export async function runBlobUpload(args) {
|
|
|
50
50
|
fail(`failed to read --file '${filePath}': ${e instanceof Error ? e.message : String(e)}`, "invalid_args");
|
|
51
51
|
}
|
|
52
52
|
const scopeRaw = args.flags.get("scope") ?? "agent";
|
|
53
|
-
if (scopeRaw !== "agent" &&
|
|
54
|
-
scopeRaw
|
|
55
|
-
scopeRaw !== "template") {
|
|
56
|
-
fail(`unknown --scope '${scopeRaw}' — expected one of: agent, surface, template`, "invalid_args");
|
|
53
|
+
if (scopeRaw !== "agent" && scopeRaw !== "pane" && scopeRaw !== "template") {
|
|
54
|
+
fail(`unknown --scope '${scopeRaw}' — expected one of: agent, pane, template`, "invalid_args");
|
|
57
55
|
}
|
|
58
56
|
const scope = scopeRaw;
|
|
59
|
-
if (scope === "
|
|
60
|
-
fail("--scope=
|
|
57
|
+
if (scope === "pane" && !args.flags.get("pane-id")) {
|
|
58
|
+
fail("--scope=pane requires --pane-id <id>", "invalid_args");
|
|
61
59
|
}
|
|
62
60
|
if (scope === "template" && !args.flags.get("template-id")) {
|
|
63
61
|
fail("--scope=template requires --template-id <id>", "invalid_args");
|
|
@@ -66,7 +64,7 @@ export async function runBlobUpload(args) {
|
|
|
66
64
|
try {
|
|
67
65
|
const ref = await client.uploadBlob(bytes, {
|
|
68
66
|
scope,
|
|
69
|
-
|
|
67
|
+
paneId: args.flags.get("pane-id"),
|
|
70
68
|
templateId: args.flags.get("template-id"),
|
|
71
69
|
filename: args.flags.get("filename") ?? basename(filePath),
|
|
72
70
|
mime: args.flags.get("mime"),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// `pane attachment` — manage binary attachments (attachments) on the relay.
|
|
2
2
|
//
|
|
3
3
|
// A attachment is a typed binary file (image, PDF, audio, video, etc.) owned by an
|
|
4
|
-
// agent and optionally bound to a
|
|
4
|
+
// agent and optionally bound to a pane or template. Pages reference attachments
|
|
5
5
|
// by id with `format: pane-attachment-id`; participants can fetch a attachment through a
|
|
6
6
|
// minted capability URL (/b/<token>) without needing the agent's API key.
|
|
7
7
|
//
|
|
@@ -24,8 +24,8 @@ export const blobHelp = `pane attachment — manage attachments (binary attachme
|
|
|
24
24
|
A attachment is a typed binary file (image, PDF, audio, video, ...) the agent has
|
|
25
25
|
uploaded to the relay. Blobs are scoped:
|
|
26
26
|
|
|
27
|
-
agent — reusable across the agent's
|
|
28
|
-
|
|
27
|
+
agent — reusable across the agent's panes (default)
|
|
28
|
+
pane — bound to one pane; deleted with it
|
|
29
29
|
template — bound to a reusable template; deleted with it
|
|
30
30
|
|
|
31
31
|
Pages reference attachments by id (the relay's schema validates the id with
|
|
@@ -37,7 +37,7 @@ Usage:
|
|
|
37
37
|
|
|
38
38
|
Verbs:
|
|
39
39
|
upload Upload a local file. Required: --file. Optional:
|
|
40
|
-
--scope, --
|
|
40
|
+
--scope, --pane-id, --template-id, --filename,
|
|
41
41
|
--mime. Prints { attachment_id, scope, mime, size, sha256,
|
|
42
42
|
... }.
|
|
43
43
|
|
|
@@ -66,12 +66,12 @@ Output: stdout is machine-readable JSON. Errors go to stderr as
|
|
|
66
66
|
* Build a new ParsedArgs with the leading positional (the verb) stripped.
|
|
67
67
|
* The downstream verb runners read their primary positional (the attachment_id)
|
|
68
68
|
* at positionals[0], so we hand them an args object that looks exactly like
|
|
69
|
-
* they were called directly — mirrors
|
|
69
|
+
* they were called directly — mirrors pane.ts's shiftPositionals.
|
|
70
70
|
*/
|
|
71
71
|
function shiftPositionals(args) {
|
|
72
72
|
// Propagate danglingValueFlags so the leaf runner's assertKnownFlags
|
|
73
73
|
// can still distinguish "unknown flag" from "missing value" — see the
|
|
74
|
-
// matching note in
|
|
74
|
+
// matching note in pane.ts's shiftPositionals.
|
|
75
75
|
const out = {
|
|
76
76
|
positionals: args.positionals.slice(1),
|
|
77
77
|
flags: args.flags,
|
|
@@ -95,7 +95,7 @@ export async function runBlob(args) {
|
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
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
|
|
98
|
+
// so the general pre-empt would already fire, but for parity with pane.ts
|
|
99
99
|
// we route through here when args carry the "list" positional explicitly).
|
|
100
100
|
if (verb === "list" &&
|
|
101
101
|
args.bools.has("help") &&
|
package/dist/commands/claim.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// 2. Alice hands the code to the agent out-of-band (this CLI invocation
|
|
8
8
|
// is exactly that handoff).
|
|
9
9
|
// 3. CLI calls POST /v1/agents/claim with the calling agent's API key.
|
|
10
|
-
// 4. Relay binds Agent.ownerHumanId = alice.id, migrates
|
|
10
|
+
// 4. Relay binds Agent.ownerHumanId = alice.id, migrates pane ownership.
|
|
11
11
|
//
|
|
12
12
|
// The CLI does NOT print the human's email or id — only the relay's response,
|
|
13
13
|
// which is { ok, owner_human_id, claimed_at }. The agent's existing API key
|
package/dist/commands/config.js
CHANGED
|
@@ -1,50 +1,262 @@
|
|
|
1
|
-
// `pane config
|
|
1
|
+
// `pane config <verb>` — inspect and manage the multi-profile CLI config.
|
|
2
|
+
//
|
|
3
|
+
// show describe the resolved (url, api_key) the CLI would use
|
|
4
|
+
// list list saved profiles (names + URLs + current marker)
|
|
5
|
+
// use <name> switch the active profile
|
|
6
|
+
// add <name> manually add a profile (for keys obtained out of band)
|
|
7
|
+
// rm <name> delete a profile
|
|
8
|
+
//
|
|
9
|
+
// All four mutating verbs operate on the multi-profile store at
|
|
10
|
+
// $XDG_CONFIG_HOME/pane/config.json. See store.ts for the layout. They make
|
|
11
|
+
// NO network calls — purely local config management.
|
|
2
12
|
import { assertKnownFlags } from "../argv.js";
|
|
3
13
|
import { describeConfig } from "../config.js";
|
|
14
|
+
import { isValidProfileName, readStore, removeProfile, setCurrentProfile, storePath, upsertProfile, } from "../store.js";
|
|
4
15
|
import { printJson, fail } from "../output.js";
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
16
|
+
const SHOW_FLAGS = [];
|
|
17
|
+
const SHOW_BOOLS = [];
|
|
18
|
+
const LIST_FLAGS = [];
|
|
19
|
+
const LIST_BOOLS = [];
|
|
20
|
+
const USE_FLAGS = [];
|
|
21
|
+
const USE_BOOLS = [];
|
|
22
|
+
const ADD_FLAGS = ["api-key"];
|
|
23
|
+
const ADD_BOOLS = [];
|
|
24
|
+
const RM_FLAGS = [];
|
|
25
|
+
const RM_BOOLS = [];
|
|
26
|
+
export const configHelp = `pane config — show and manage the CLI config (multi-profile)
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
pane config show Show the resolved relay config
|
|
30
|
+
pane config list List saved profiles
|
|
31
|
+
pane config use <profile> Switch the active profile
|
|
32
|
+
pane config add <profile> --url <u> --api-key <k>
|
|
33
|
+
Add a profile manually
|
|
34
|
+
pane config rm <profile> Delete a profile
|
|
35
|
+
|
|
36
|
+
A profile is one (url, api_key) pair under a short name (dev, staging, prod).
|
|
37
|
+
Switch via 'pane config use', '--profile <name>', or the PANE_PROFILE env var.
|
|
38
|
+
The active profile's (url, api_key) is what every other command sees unless
|
|
39
|
+
overridden by --url / --api-key or PANE_URL / PANE_API_KEY.
|
|
40
|
+
|
|
41
|
+
Run \`pane config <verb> --help\` for verb-specific help. The full API key is
|
|
42
|
+
never printed; only a short masked prefix.
|
|
43
|
+
|
|
44
|
+
The config file lives at \${XDG_CONFIG_HOME:-~/.config}/pane/config.json
|
|
45
|
+
(mode 0600).`;
|
|
46
|
+
const showHelp = `pane config show — show the resolved relay config
|
|
8
47
|
|
|
9
48
|
Usage:
|
|
10
49
|
pane config show [options]
|
|
11
50
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
purely inspects flags, env vars, and the saved config
|
|
16
|
-
file.
|
|
51
|
+
Reports the (url, api_key) the CLI would use right now, and where each value
|
|
52
|
+
came from (flag / env / profile / none). Purely inspects flags + env + the
|
|
53
|
+
saved config file; makes NO network call.
|
|
17
54
|
|
|
18
|
-
The API key is never printed in full
|
|
55
|
+
The API key is never printed in full — only a short masked prefix.
|
|
19
56
|
|
|
20
57
|
Options:
|
|
21
58
|
--url <url> Relay base URL (overrides PANE_URL) — affects the report.
|
|
22
|
-
--api-key <key> Agent API key (overrides PANE_API_KEY) — affects the
|
|
23
|
-
|
|
59
|
+
--api-key <key> Agent API key (overrides PANE_API_KEY) — affects the report.
|
|
60
|
+
--profile <name> Profile to resolve against — affects the report.
|
|
61
|
+
-h, --help Show this help.
|
|
62
|
+
|
|
63
|
+
Output (stdout, JSON):
|
|
64
|
+
{
|
|
65
|
+
url, url_source, flag | env | profile | none
|
|
66
|
+
key_prefix, key_source, flag | env | profile | none
|
|
67
|
+
profile, profile_source, active profile name + how it was chosen
|
|
68
|
+
config_path
|
|
69
|
+
}`;
|
|
70
|
+
const listHelp = `pane config list — list saved profiles
|
|
71
|
+
|
|
72
|
+
Usage:
|
|
73
|
+
pane config list [options]
|
|
74
|
+
|
|
75
|
+
Prints every profile in the local config file, with its URL and a masked
|
|
76
|
+
key prefix. The active profile carries 'current: true'.
|
|
77
|
+
|
|
78
|
+
Options:
|
|
24
79
|
-h, --help Show this help.
|
|
25
80
|
|
|
26
81
|
Output (stdout, JSON):
|
|
27
82
|
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
key_source, "flag" | "env" | "store" | "none"
|
|
32
|
-
config_path absolute path to the CLI config file
|
|
83
|
+
current: <name|null>,
|
|
84
|
+
profiles: [ { name, url, key_prefix, current }, … ],
|
|
85
|
+
config_path
|
|
33
86
|
}`;
|
|
87
|
+
const useHelp = `pane config use <profile> — switch the active profile
|
|
88
|
+
|
|
89
|
+
Usage:
|
|
90
|
+
pane config use <profile>
|
|
91
|
+
|
|
92
|
+
Sets 'current_profile' in the config file. The named profile must exist
|
|
93
|
+
(create it first with 'pane agent register --profile <name>' or
|
|
94
|
+
'pane config add <name>').
|
|
95
|
+
|
|
96
|
+
Options:
|
|
97
|
+
-h, --help Show this help.
|
|
98
|
+
|
|
99
|
+
Output (stdout, JSON):
|
|
100
|
+
{ profile, saved_to }`;
|
|
101
|
+
const addHelp = `pane config add <profile> — add a profile manually
|
|
102
|
+
|
|
103
|
+
Usage:
|
|
104
|
+
pane config add <profile> --url <url> --api-key <api-key>
|
|
105
|
+
|
|
106
|
+
Saves a (url, api_key) pair under <profile> without contacting the relay.
|
|
107
|
+
Use this when an operator handed you an API key out of band (e.g. a closed-
|
|
108
|
+
registration relay) — for self-register and secret-mode relays, prefer
|
|
109
|
+
'pane agent register --profile <name>'.
|
|
110
|
+
|
|
111
|
+
If <profile> already exists, the existing values are overwritten.
|
|
112
|
+
|
|
113
|
+
Options:
|
|
114
|
+
--url <url> Relay base URL. REQUIRED.
|
|
115
|
+
--api-key <key> Agent API key. REQUIRED.
|
|
116
|
+
-h, --help Show this help.
|
|
117
|
+
|
|
118
|
+
Output (stdout, JSON):
|
|
119
|
+
{ profile, saved_to }
|
|
120
|
+
|
|
121
|
+
Does NOT change 'current_profile' unless this is the first profile being
|
|
122
|
+
added. Use 'pane config use' afterwards to switch.`;
|
|
123
|
+
const rmHelp = `pane config rm <profile> — delete a profile
|
|
124
|
+
|
|
125
|
+
Usage:
|
|
126
|
+
pane config rm <profile>
|
|
127
|
+
|
|
128
|
+
Removes the named profile from the config file. If it was the active profile,
|
|
129
|
+
'current_profile' is cleared (the next command falls back to env / default
|
|
130
|
+
URL until another profile is selected via --profile or 'pane config use').
|
|
131
|
+
|
|
132
|
+
Options:
|
|
133
|
+
-h, --help Show this help.
|
|
134
|
+
|
|
135
|
+
Output (stdout, JSON):
|
|
136
|
+
{ profile, was_current, path }`;
|
|
34
137
|
async function runConfigShow(args) {
|
|
35
|
-
assertKnownFlags(args,
|
|
138
|
+
assertKnownFlags(args, SHOW_FLAGS, SHOW_BOOLS, "pane config show");
|
|
36
139
|
printJson(describeConfig(args));
|
|
37
140
|
}
|
|
141
|
+
function maskKey(key) {
|
|
142
|
+
if (!key)
|
|
143
|
+
return null;
|
|
144
|
+
if (key.startsWith("pane_") && key.length >= 11)
|
|
145
|
+
return key.slice(0, 11) + "…";
|
|
146
|
+
return key.slice(0, 8) + "…";
|
|
147
|
+
}
|
|
148
|
+
async function runConfigList(args) {
|
|
149
|
+
assertKnownFlags(args, LIST_FLAGS, LIST_BOOLS, "pane config list");
|
|
150
|
+
const store = readStore();
|
|
151
|
+
const profiles = Object.entries(store.profiles)
|
|
152
|
+
.map(([name, p]) => ({
|
|
153
|
+
name,
|
|
154
|
+
url: p.url ?? null,
|
|
155
|
+
key_prefix: maskKey(p.apiKey),
|
|
156
|
+
current: name === store.currentProfile,
|
|
157
|
+
}))
|
|
158
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
159
|
+
printJson({
|
|
160
|
+
current: store.currentProfile ?? null,
|
|
161
|
+
profiles,
|
|
162
|
+
config_path: storePath(),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
async function runConfigUse(args) {
|
|
166
|
+
assertKnownFlags(args, USE_FLAGS, USE_BOOLS, "pane config use");
|
|
167
|
+
const name = args.positionals[1];
|
|
168
|
+
if (!name) {
|
|
169
|
+
fail("missing profile name — usage: pane config use <profile>", "invalid_args");
|
|
170
|
+
}
|
|
171
|
+
let savedTo;
|
|
172
|
+
try {
|
|
173
|
+
savedTo = setCurrentProfile(name);
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
fail(e instanceof Error ? e.message : String(e), "config_error");
|
|
177
|
+
}
|
|
178
|
+
printJson({ profile: name, saved_to: savedTo });
|
|
179
|
+
}
|
|
180
|
+
async function runConfigAdd(args) {
|
|
181
|
+
assertKnownFlags(args, ADD_FLAGS, ADD_BOOLS, "pane config add");
|
|
182
|
+
const name = args.positionals[1];
|
|
183
|
+
if (!name) {
|
|
184
|
+
fail("missing profile name — usage: pane config add <profile> --url <url> --api-key <key>", "invalid_args");
|
|
185
|
+
}
|
|
186
|
+
if (!isValidProfileName(name)) {
|
|
187
|
+
fail(`invalid profile name '${name}' — letters, digits, _ and -, up to 32 chars`, "invalid_args");
|
|
188
|
+
}
|
|
189
|
+
const url = args.flags.get("url");
|
|
190
|
+
const apiKey = args.flags.get("api-key");
|
|
191
|
+
if (!url) {
|
|
192
|
+
fail("--url is required — usage: pane config add <profile> --url <url> --api-key <key>", "invalid_args");
|
|
193
|
+
}
|
|
194
|
+
if (!apiKey) {
|
|
195
|
+
fail("--api-key is required — usage: pane config add <profile> --url <url> --api-key <key>", "invalid_args");
|
|
196
|
+
}
|
|
197
|
+
// setCurrent=false: adding a profile shouldn't silently switch the user
|
|
198
|
+
// off whatever they were on. They use `pane config use` after to flip.
|
|
199
|
+
// EXCEPT: if there's no current profile yet (first add), upsertProfile
|
|
200
|
+
// sets it automatically — that's the correct fresh-install behaviour.
|
|
201
|
+
const savedTo = upsertProfile(name, { url: url.replace(/\/$/, ""), apiKey }, false);
|
|
202
|
+
printJson({ profile: name, saved_to: savedTo });
|
|
203
|
+
}
|
|
204
|
+
async function runConfigRm(args) {
|
|
205
|
+
assertKnownFlags(args, RM_FLAGS, RM_BOOLS, "pane config rm");
|
|
206
|
+
const name = args.positionals[1];
|
|
207
|
+
if (!name) {
|
|
208
|
+
fail("missing profile name — usage: pane config rm <profile>", "invalid_args");
|
|
209
|
+
}
|
|
210
|
+
let result;
|
|
211
|
+
try {
|
|
212
|
+
result = removeProfile(name);
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
fail(e instanceof Error ? e.message : String(e), "config_error");
|
|
216
|
+
}
|
|
217
|
+
printJson({
|
|
218
|
+
profile: name,
|
|
219
|
+
was_current: result.was_current,
|
|
220
|
+
path: result.path,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
38
223
|
export async function runConfig(args) {
|
|
39
224
|
const verb = args.positionals[0];
|
|
225
|
+
// Per-verb --help: 'pane config show --help' etc. Caught before dispatch
|
|
226
|
+
// so each runner doesn't need to repeat the check.
|
|
227
|
+
if (args.bools.has("help") && verb !== undefined) {
|
|
228
|
+
const helps = {
|
|
229
|
+
show: showHelp,
|
|
230
|
+
list: listHelp,
|
|
231
|
+
use: useHelp,
|
|
232
|
+
add: addHelp,
|
|
233
|
+
rm: rmHelp,
|
|
234
|
+
};
|
|
235
|
+
if (helps[verb] !== undefined) {
|
|
236
|
+
process.stdout.write(helps[verb] + "\n");
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
40
240
|
switch (verb) {
|
|
41
241
|
case "show":
|
|
42
242
|
await runConfigShow(args);
|
|
43
243
|
break;
|
|
244
|
+
case "list":
|
|
245
|
+
await runConfigList(args);
|
|
246
|
+
break;
|
|
247
|
+
case "use":
|
|
248
|
+
await runConfigUse(args);
|
|
249
|
+
break;
|
|
250
|
+
case "add":
|
|
251
|
+
await runConfigAdd(args);
|
|
252
|
+
break;
|
|
253
|
+
case "rm":
|
|
254
|
+
await runConfigRm(args);
|
|
255
|
+
break;
|
|
44
256
|
case undefined:
|
|
45
|
-
fail("missing verb — usage: pane config show (run 'pane config --help')", "invalid_args");
|
|
257
|
+
fail("missing verb — usage: pane config <show|list|use|add|rm> (run 'pane config --help')", "invalid_args");
|
|
46
258
|
break;
|
|
47
259
|
default:
|
|
48
|
-
fail(`unknown config verb '${verb}' — expected show (run 'pane config --help')`, "invalid_args");
|
|
260
|
+
fail(`unknown config verb '${verb}' — expected show|list|use|add|rm (run 'pane config --help')`, "invalid_args");
|
|
49
261
|
}
|
|
50
262
|
}
|