@pipod/cli 0.1.18 → 0.1.20
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 +19 -0
- package/dist/chunk-7TJWBDVX.js +100 -0
- package/dist/chunk-7TJWBDVX.js.map +1 -0
- package/dist/cli.js +35 -95
- package/dist/cli.js.map +1 -1
- package/dist/key-repeat-guard-7OVKW5KD.js +64 -0
- package/dist/key-repeat-guard-7OVKW5KD.js.map +1 -0
- package/dist/stdin-fragment-guard-SPIJC7GT.js +61 -0
- package/dist/stdin-fragment-guard-SPIJC7GT.js.map +1 -0
- package/docs/classic-runtime-coupling.md +12 -0
- package/docs/keyboard-input.md +91 -0
- package/man/pipod-config.5 +1 -1
- package/man/pipod.1 +4 -1
- package/man/pipod.1.md +3 -0
- package/npm-shrinkwrap.json +4237 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -21,6 +21,25 @@ The npm release works with any server at least as new as it; a CLI newer than it
|
|
|
21
21
|
refused, so upgrade the server first. Building the CLI from the server's own checkout always
|
|
22
22
|
matches.
|
|
23
23
|
|
|
24
|
+
## Release dependencies
|
|
25
|
+
|
|
26
|
+
The CLI is an application package: its committed `npm-shrinkwrap.json` ships in
|
|
27
|
+
npm releases so fresh installs and `pipod update` use the same dependency graph
|
|
28
|
+
validated by `npm ci`. A plain `package-lock.json` is not published and would let
|
|
29
|
+
Pi's transitive semver ranges drift, even with Pi itself pinned exactly. For
|
|
30
|
+
example, pi-coding-agent 1.0.2 imports exports removed by pi-codemode 1.2.0.
|
|
31
|
+
|
|
32
|
+
Keep the shrinkwrap when updating dependencies (`make bump-pi VERSION=x.y.z` for
|
|
33
|
+
Pi). Do not replace it with package-level `overrides`: npm ignores those when this
|
|
34
|
+
package is installed as a dependency.
|
|
35
|
+
|
|
36
|
+
Install releases from the registry (`npm install -g @pipod/cli` or `pipod update`).
|
|
37
|
+
Direct `.tgz` installs with npm 10/11 lack the registry's shrinkwrap metadata and
|
|
38
|
+
can still resolve newer transitive dependencies. To verify a packed release
|
|
39
|
+
before publishing, extract it into a clean directory, run `npm ci --omit=dev`
|
|
40
|
+
there, and run `node dist/cli.js --version`; after publishing, verify a fresh
|
|
41
|
+
registry install as well.
|
|
42
|
+
|
|
24
43
|
## Secrets
|
|
25
44
|
|
|
26
45
|
```bash
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/log.ts
|
|
2
|
+
import { stripVTControlCharacters } from "util";
|
|
3
|
+
|
|
4
|
+
// src/redact.ts
|
|
5
|
+
var MIN_REDACTABLE_LENGTH = 4;
|
|
6
|
+
var secrets = /* @__PURE__ */ new Set();
|
|
7
|
+
function registerSecret(value) {
|
|
8
|
+
if (!value) return;
|
|
9
|
+
const v = String(value);
|
|
10
|
+
if (v.length < MIN_REDACTABLE_LENGTH) return;
|
|
11
|
+
secrets.add(v);
|
|
12
|
+
}
|
|
13
|
+
function registerSecrets(values) {
|
|
14
|
+
for (const v of Object.values(values)) registerSecret(v);
|
|
15
|
+
}
|
|
16
|
+
function redact(input) {
|
|
17
|
+
if (secrets.size === 0) return input;
|
|
18
|
+
let out2 = input;
|
|
19
|
+
for (const secret of [...secrets].sort((a, b) => b.length - a.length)) {
|
|
20
|
+
if (out2.includes(secret)) out2 = out2.split(secret).join("[redacted]");
|
|
21
|
+
}
|
|
22
|
+
return out2;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/log.ts
|
|
26
|
+
var verbose = false;
|
|
27
|
+
var colorEnabled = process.stderr.isTTY === true && !process.env.NO_COLOR;
|
|
28
|
+
function setVerbose(v) {
|
|
29
|
+
verbose = v;
|
|
30
|
+
}
|
|
31
|
+
function isVerbose() {
|
|
32
|
+
return verbose;
|
|
33
|
+
}
|
|
34
|
+
var CODES = {
|
|
35
|
+
reset: "\x1B[0m",
|
|
36
|
+
dim: "\x1B[2m",
|
|
37
|
+
bold: "\x1B[1m",
|
|
38
|
+
red: "\x1B[31m",
|
|
39
|
+
yellow: "\x1B[33m",
|
|
40
|
+
cyan: "\x1B[36m",
|
|
41
|
+
green: "\x1B[32m"
|
|
42
|
+
};
|
|
43
|
+
function paint(code, s) {
|
|
44
|
+
return colorEnabled ? `${CODES[code]}${s}${CODES.reset}` : s;
|
|
45
|
+
}
|
|
46
|
+
var color = {
|
|
47
|
+
dim: (s) => paint("dim", s),
|
|
48
|
+
bold: (s) => paint("bold", s),
|
|
49
|
+
red: (s) => paint("red", s),
|
|
50
|
+
yellow: (s) => paint("yellow", s),
|
|
51
|
+
cyan: (s) => paint("cyan", s),
|
|
52
|
+
green: (s) => paint("green", s)
|
|
53
|
+
};
|
|
54
|
+
function write(line) {
|
|
55
|
+
process.stderr.write(redact(line) + "\n");
|
|
56
|
+
}
|
|
57
|
+
function info(msg) {
|
|
58
|
+
write(`${paint("cyan", "pi pod")} ${msg}`);
|
|
59
|
+
}
|
|
60
|
+
function step(phase, msg) {
|
|
61
|
+
write(`${paint("cyan", "pi pod")} ${paint("dim", `[${phase}]`)} ${msg}`);
|
|
62
|
+
}
|
|
63
|
+
function warn(msg) {
|
|
64
|
+
write(`${paint("yellow", "warning")} ${msg}`);
|
|
65
|
+
}
|
|
66
|
+
function error(msg) {
|
|
67
|
+
write(`${paint("red", "error")} ${msg}`);
|
|
68
|
+
}
|
|
69
|
+
function hint(msg) {
|
|
70
|
+
const lines = msg.split("\n");
|
|
71
|
+
write(`${paint("dim", " hint:")} ${lines[0] ?? ""}`);
|
|
72
|
+
for (const line of lines.slice(1)) write(paint("dim", ` ${line}`));
|
|
73
|
+
}
|
|
74
|
+
function debug(msg) {
|
|
75
|
+
if (verbose) write(`${paint("dim", "debug")} ${paint("dim", msg)}`);
|
|
76
|
+
}
|
|
77
|
+
function plain(msg) {
|
|
78
|
+
write(msg);
|
|
79
|
+
}
|
|
80
|
+
function out(msg) {
|
|
81
|
+
const line = redact(msg);
|
|
82
|
+
process.stdout.write((process.stdout.isTTY === true ? line : stripVTControlCharacters(line)) + "\n");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
registerSecret,
|
|
87
|
+
registerSecrets,
|
|
88
|
+
setVerbose,
|
|
89
|
+
isVerbose,
|
|
90
|
+
color,
|
|
91
|
+
info,
|
|
92
|
+
step,
|
|
93
|
+
warn,
|
|
94
|
+
error,
|
|
95
|
+
hint,
|
|
96
|
+
debug,
|
|
97
|
+
plain,
|
|
98
|
+
out
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=chunk-7TJWBDVX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/log.ts","../src/redact.ts"],"sourcesContent":["/**\n * src/log.ts — the launcher's logger. Everything user-visible goes through here so that\n * redaction (§7.3) is structural rather than something each call site has to remember.\n */\nimport { stripVTControlCharacters } from \"node:util\";\nimport { redact } from \"./redact.js\";\n\nlet verbose = false;\nlet colorEnabled = process.stderr.isTTY === true && !process.env.NO_COLOR;\n\nexport function setVerbose(v: boolean): void {\n verbose = v;\n}\n\nexport function isVerbose(): boolean {\n return verbose;\n}\n\nexport function setColor(enabled: boolean): void {\n colorEnabled = enabled;\n}\n\nconst CODES = {\n reset: \"\\u001b[0m\",\n dim: \"\\u001b[2m\",\n bold: \"\\u001b[1m\",\n red: \"\\u001b[31m\",\n yellow: \"\\u001b[33m\",\n cyan: \"\\u001b[36m\",\n green: \"\\u001b[32m\",\n} as const;\n\nfunction paint(code: keyof typeof CODES, s: string): string {\n return colorEnabled ? `${CODES[code]}${s}${CODES.reset}` : s;\n}\n\nexport const color = {\n dim: (s: string) => paint(\"dim\", s),\n bold: (s: string) => paint(\"bold\", s),\n red: (s: string) => paint(\"red\", s),\n yellow: (s: string) => paint(\"yellow\", s),\n cyan: (s: string) => paint(\"cyan\", s),\n green: (s: string) => paint(\"green\", s),\n};\n\n/**\n * Diagnostics go to stderr so that stdout stays clean for the PTY stream and for\n * machine-readable output (`--dry-run`, `--version`).\n */\nfunction write(line: string): void {\n process.stderr.write(redact(line) + \"\\n\");\n}\n\nexport function info(msg: string): void {\n write(`${paint(\"cyan\", \"pi pod\")} ${msg}`);\n}\n\nexport function step(phase: string, msg: string): void {\n write(`${paint(\"cyan\", \"pi pod\")} ${paint(\"dim\", `[${phase}]`)} ${msg}`);\n}\n\nexport function warn(msg: string): void {\n write(`${paint(\"yellow\", \"warning\")} ${msg}`);\n}\n\nexport function error(msg: string): void {\n write(`${paint(\"red\", \"error\")} ${msg}`);\n}\n\n/** Multi-line hints are one hint: the prefix appears once, continuation lines align under it. */\nexport function hint(msg: string): void {\n const lines = msg.split(\"\\n\");\n write(`${paint(\"dim\", \" hint:\")} ${lines[0] ?? \"\"}`);\n for (const line of lines.slice(1)) write(paint(\"dim\", ` ${line}`));\n}\n\nexport function debug(msg: string): void {\n if (verbose) write(`${paint(\"dim\", \"debug\")} ${paint(\"dim\", msg)}`);\n}\n\nexport function plain(msg: string): void {\n write(msg);\n}\n\n/**\n * Direct stdout writer for command results and machine-readable output; still redacted.\n * Color is useful at a terminal but must never leak into a pipe or redirected file.\n */\nexport function out(msg: string): void {\n const line = redact(msg);\n process.stdout.write((process.stdout.isTTY === true ? line : stripVTControlCharacters(line)) + \"\\n\");\n}\n\n/**\n * Wall-clock phase timings for one launch/attach, so the summary line can say where the\n * time went. Overlapping (parallelized) phases each record their own duration; the summary\n * total is wall time, not the sum.\n */\nexport interface PhaseTiming {\n phase: string;\n ms: number;\n}\n\nconst phaseTimings: PhaseTiming[] = [];\n\nexport function resetPhaseTimings(): void {\n phaseTimings.length = 0;\n}\n\nexport function recordPhase(phase: string, ms: number): void {\n phaseTimings.push({ phase, ms });\n}\n\nexport async function timePhase<T>(phase: string, work: () => Promise<T>): Promise<T> {\n const startedAt = Date.now();\n try {\n return await work();\n } finally {\n recordPhase(phase, Date.now() - startedAt);\n }\n}\n\nfunction fmtSeconds(ms: number): string {\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\n/** One line naming total wall time and every phase that cost at least 100ms. */\nexport function phaseSummary(label: string, totalMs: number): string {\n const parts = phaseTimings.filter((t) => t.ms >= 100).map((t) => `${t.phase} ${fmtSeconds(t.ms)}`);\n const breakdown = parts.length > 0 ? ` (${parts.join(\" · \")})` : \"\";\n return `${label} in ${fmtSeconds(totalMs)}${breakdown}`;\n}\n\n/**\n * Print a periodic \"still …\" line while a long provider operation runs, so a multi-minute\n * wait is distinguishable from a hang. The line names the elapsed time and, when known,\n * roughly how long the wait usually takes.\n */\nexport const LIVENESS_INTERVAL_MS = 15_000;\n\nexport async function withLiveness<T>(\n label: string,\n work: () => Promise<T>,\n opts: { intervalMs?: number; expectation?: string } = {},\n): Promise<T> {\n const intervalMs = opts.intervalMs ?? LIVENESS_INTERVAL_MS;\n const startedAt = Date.now();\n const timer = setInterval(() => {\n const elapsed = Math.round((Date.now() - startedAt) / 1000);\n const usually = opts.expectation ? `, ${opts.expectation}` : \"\";\n write(`${paint(\"cyan\", \"pi pod\")} ${paint(\"dim\", `still ${label} (${elapsed}s elapsed${usually})`)}`);\n }, intervalMs);\n timer.unref?.();\n try {\n return await work();\n } finally {\n clearInterval(timer);\n }\n}\n\n/**\n * Stream a child stream to the terminal with a prefix (`[init]`, §4.3). Handles partial\n * lines so a chunk boundary never splits a prefix.\n */\nexport function prefixedStreamer(prefix: string): (chunk: Uint8Array) => void {\n let pending = \"\";\n const flushLine = (line: string) => write(`${paint(\"dim\", prefix)} ${line}`);\n return (chunk: Uint8Array) => {\n pending += Buffer.from(chunk).toString(\"utf8\");\n const lines = pending.split(/\\r?\\n/);\n pending = lines.pop() ?? \"\";\n for (const line of lines) flushLine(line);\n // Guard against a runaway line with no newline ever arriving.\n if (pending.length > 8192) {\n flushLine(pending);\n pending = \"\";\n }\n };\n}\n","/**\n * src/redact.ts — secret-aware log redaction (§7.3).\n *\n * Every value originating from the env file is registered here; the logger runs all output\n * through `redact()`. The conformance suite (§14) asserts that no registered value ever\n * appears in captured logs.\n */\n\n/** Values short enough to appear coincidentally are not worth redacting (and would mangle output). */\nconst MIN_REDACTABLE_LENGTH = 4;\n\nconst secrets = new Set<string>();\n\nexport function registerSecret(value: string | undefined | null): void {\n if (!value) return;\n const v = String(value);\n if (v.length < MIN_REDACTABLE_LENGTH) return;\n secrets.add(v);\n}\n\nexport function registerSecrets(values: Record<string, string>): void {\n for (const v of Object.values(values)) registerSecret(v);\n}\n\n/** Test/reset hook. */\nexport function clearSecrets(): void {\n secrets.clear();\n}\n\nexport function registeredSecretCount(): number {\n return secrets.size;\n}\n\n/**\n * Replace every registered secret with `[redacted]`. Longest-first so that a secret which\n * contains another secret as a substring is not partially revealed.\n */\nexport function redact(input: string): string {\n if (secrets.size === 0) return input;\n let out = input;\n for (const secret of [...secrets].sort((a, b) => b.length - a.length)) {\n if (out.includes(secret)) out = out.split(secret).join(\"[redacted]\");\n }\n return out;\n}\n\nexport function redactBytes(input: Uint8Array): Uint8Array {\n if (secrets.size === 0) return input;\n const text = Buffer.from(input).toString(\"utf8\");\n const cleaned = redact(text);\n return cleaned === text ? input : Buffer.from(cleaned, \"utf8\");\n}\n\n/** Redact an arbitrary structure for debug dumps. Keys are preserved, values scrubbed. */\nexport function redactDeep<T>(value: T): T {\n if (typeof value === \"string\") return redact(value) as unknown as T;\n if (Array.isArray(value)) return value.map((v) => redactDeep(v)) as unknown as T;\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = redactDeep(v);\n return out as unknown as T;\n }\n return value;\n}\n"],"mappings":"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","names":["out"]}
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,21 @@ import {
|
|
|
4
4
|
POD_SUBCOMMANDS,
|
|
5
5
|
PodCommandRouter
|
|
6
6
|
} from "./chunk-GNKF75X7.js";
|
|
7
|
+
import {
|
|
8
|
+
color,
|
|
9
|
+
debug,
|
|
10
|
+
error,
|
|
11
|
+
hint,
|
|
12
|
+
info,
|
|
13
|
+
isVerbose,
|
|
14
|
+
out,
|
|
15
|
+
plain,
|
|
16
|
+
registerSecret,
|
|
17
|
+
registerSecrets,
|
|
18
|
+
setVerbose,
|
|
19
|
+
step,
|
|
20
|
+
warn
|
|
21
|
+
} from "./chunk-7TJWBDVX.js";
|
|
7
22
|
|
|
8
23
|
// src/cli.ts
|
|
9
24
|
import * as fs36 from "fs";
|
|
@@ -96,90 +111,6 @@ var PodAsleepError = class extends PiPodError {
|
|
|
96
111
|
import * as fs7 from "fs";
|
|
97
112
|
import { Readable } from "stream";
|
|
98
113
|
|
|
99
|
-
// src/log.ts
|
|
100
|
-
import { stripVTControlCharacters } from "util";
|
|
101
|
-
|
|
102
|
-
// src/redact.ts
|
|
103
|
-
var MIN_REDACTABLE_LENGTH = 4;
|
|
104
|
-
var secrets = /* @__PURE__ */ new Set();
|
|
105
|
-
function registerSecret(value) {
|
|
106
|
-
if (!value) return;
|
|
107
|
-
const v = String(value);
|
|
108
|
-
if (v.length < MIN_REDACTABLE_LENGTH) return;
|
|
109
|
-
secrets.add(v);
|
|
110
|
-
}
|
|
111
|
-
function registerSecrets(values) {
|
|
112
|
-
for (const v of Object.values(values)) registerSecret(v);
|
|
113
|
-
}
|
|
114
|
-
function redact(input) {
|
|
115
|
-
if (secrets.size === 0) return input;
|
|
116
|
-
let out2 = input;
|
|
117
|
-
for (const secret of [...secrets].sort((a, b) => b.length - a.length)) {
|
|
118
|
-
if (out2.includes(secret)) out2 = out2.split(secret).join("[redacted]");
|
|
119
|
-
}
|
|
120
|
-
return out2;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// src/log.ts
|
|
124
|
-
var verbose = false;
|
|
125
|
-
var colorEnabled = process.stderr.isTTY === true && !process.env.NO_COLOR;
|
|
126
|
-
function setVerbose(v) {
|
|
127
|
-
verbose = v;
|
|
128
|
-
}
|
|
129
|
-
function isVerbose() {
|
|
130
|
-
return verbose;
|
|
131
|
-
}
|
|
132
|
-
var CODES = {
|
|
133
|
-
reset: "\x1B[0m",
|
|
134
|
-
dim: "\x1B[2m",
|
|
135
|
-
bold: "\x1B[1m",
|
|
136
|
-
red: "\x1B[31m",
|
|
137
|
-
yellow: "\x1B[33m",
|
|
138
|
-
cyan: "\x1B[36m",
|
|
139
|
-
green: "\x1B[32m"
|
|
140
|
-
};
|
|
141
|
-
function paint(code, s) {
|
|
142
|
-
return colorEnabled ? `${CODES[code]}${s}${CODES.reset}` : s;
|
|
143
|
-
}
|
|
144
|
-
var color = {
|
|
145
|
-
dim: (s) => paint("dim", s),
|
|
146
|
-
bold: (s) => paint("bold", s),
|
|
147
|
-
red: (s) => paint("red", s),
|
|
148
|
-
yellow: (s) => paint("yellow", s),
|
|
149
|
-
cyan: (s) => paint("cyan", s),
|
|
150
|
-
green: (s) => paint("green", s)
|
|
151
|
-
};
|
|
152
|
-
function write(line) {
|
|
153
|
-
process.stderr.write(redact(line) + "\n");
|
|
154
|
-
}
|
|
155
|
-
function info(msg) {
|
|
156
|
-
write(`${paint("cyan", "pi pod")} ${msg}`);
|
|
157
|
-
}
|
|
158
|
-
function step(phase, msg) {
|
|
159
|
-
write(`${paint("cyan", "pi pod")} ${paint("dim", `[${phase}]`)} ${msg}`);
|
|
160
|
-
}
|
|
161
|
-
function warn(msg) {
|
|
162
|
-
write(`${paint("yellow", "warning")} ${msg}`);
|
|
163
|
-
}
|
|
164
|
-
function error(msg) {
|
|
165
|
-
write(`${paint("red", "error")} ${msg}`);
|
|
166
|
-
}
|
|
167
|
-
function hint(msg) {
|
|
168
|
-
const lines = msg.split("\n");
|
|
169
|
-
write(`${paint("dim", " hint:")} ${lines[0] ?? ""}`);
|
|
170
|
-
for (const line of lines.slice(1)) write(paint("dim", ` ${line}`));
|
|
171
|
-
}
|
|
172
|
-
function debug(msg) {
|
|
173
|
-
if (verbose) write(`${paint("dim", "debug")} ${paint("dim", msg)}`);
|
|
174
|
-
}
|
|
175
|
-
function plain(msg) {
|
|
176
|
-
write(msg);
|
|
177
|
-
}
|
|
178
|
-
function out(msg) {
|
|
179
|
-
const line = redact(msg);
|
|
180
|
-
process.stdout.write((process.stdout.isTTY === true ? line : stripVTControlCharacters(line)) + "\n");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
114
|
// src/account/oidc.ts
|
|
184
115
|
import { createHash, randomBytes, timingSafeEqual } from "crypto";
|
|
185
116
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
@@ -5284,19 +5215,19 @@ async function archiveIntoPod(opts, plan, flags = {}) {
|
|
|
5284
5215
|
const ignored = gitIgnoredPaths(plan.root);
|
|
5285
5216
|
const tmpDir = opts.tmpDir ?? os3.tmpdir();
|
|
5286
5217
|
const archivePath = path14.join(tmpDir, `pi-pod-seed-${randomBytes3(8).toString("hex")}.tar.gz`);
|
|
5287
|
-
const
|
|
5218
|
+
const write = opts.tarball ?? createTarball;
|
|
5288
5219
|
try {
|
|
5289
5220
|
let summary;
|
|
5290
5221
|
let includeGit = plan.includeGit;
|
|
5291
5222
|
step("seed", `archiving ${plan.root}${includeGit ? " with .git history" : ""} (${plan.reason})`);
|
|
5292
5223
|
try {
|
|
5293
|
-
summary = await
|
|
5224
|
+
summary = await write(tarballOptions(opts, plan.root, archivePath, ignored, includeGit));
|
|
5294
5225
|
} catch (error2) {
|
|
5295
5226
|
throwIfCancelled(opts, error2);
|
|
5296
5227
|
if (!(error2 instanceof TarballLimitError) || !includeGit) throw error2;
|
|
5297
5228
|
warn(`${error2.message} \u2014 retrying without .git history`);
|
|
5298
5229
|
includeGit = false;
|
|
5299
|
-
summary = await
|
|
5230
|
+
summary = await write(tarballOptions(opts, plan.root, archivePath, ignored, includeGit));
|
|
5300
5231
|
}
|
|
5301
5232
|
if (summary.entries === 0) {
|
|
5302
5233
|
info("nothing to seed \u2014 the local directory is empty");
|
|
@@ -14346,9 +14277,9 @@ async function runAccountInteractive(opts) {
|
|
|
14346
14277
|
latestConnectionState = state;
|
|
14347
14278
|
noteAsleep(state);
|
|
14348
14279
|
if (runtimeRef) {
|
|
14349
|
-
const
|
|
14350
|
-
if (runtimeRef.bridge.ui)
|
|
14351
|
-
else runtimeRef.bridge.onUiBound(
|
|
14280
|
+
const paint = () => applyConnectionStatus(runtimeRef?.bridge.ui, state, rpc?.podId ?? opts.pod.id);
|
|
14281
|
+
if (runtimeRef.bridge.ui) paint();
|
|
14282
|
+
else runtimeRef.bridge.onUiBound(paint);
|
|
14352
14283
|
if (state.kind === "lost") {
|
|
14353
14284
|
runtimeRef.bridge.notify(
|
|
14354
14285
|
state.reason ? `could not reconnect: ${state.reason}` : "connection lost",
|
|
@@ -14694,20 +14625,27 @@ ${choices.join("\n")}`,
|
|
|
14694
14625
|
};
|
|
14695
14626
|
let unwireDisconnectedQuit = () => {
|
|
14696
14627
|
};
|
|
14628
|
+
let unwireKeyRepeat = () => {
|
|
14629
|
+
};
|
|
14697
14630
|
let mode;
|
|
14698
14631
|
try {
|
|
14699
14632
|
await runtime.init();
|
|
14700
14633
|
const { InteractiveMode } = await import("@earendil-works/pi-coding-agent");
|
|
14701
14634
|
const { wirePodChords } = await import("./chords-SOJBLC6R.js");
|
|
14635
|
+
const { wireKeyRepeatGuard } = await import("./key-repeat-guard-7OVKW5KD.js");
|
|
14636
|
+
const { guardStdinFragments } = await import("./stdin-fragment-guard-SPIJC7GT.js");
|
|
14637
|
+
guardStdinFragments();
|
|
14702
14638
|
const chords = context.config.pi.chords;
|
|
14703
14639
|
runtime.bridge.setOnUiBound(() => {
|
|
14704
14640
|
unwireChords();
|
|
14705
14641
|
unwireDisconnectedQuit();
|
|
14642
|
+
unwireKeyRepeat();
|
|
14706
14643
|
unwireChords = wirePodChords(runtime.bridge, runtime.router, chords);
|
|
14707
14644
|
unwireDisconnectedQuit = wireDisconnectedQuit(
|
|
14708
14645
|
runtime.bridge,
|
|
14709
14646
|
() => connectionAllowsQuit(latestConnectionState)
|
|
14710
14647
|
);
|
|
14648
|
+
unwireKeyRepeat = wireKeyRepeatGuard(runtime.bridge.ui);
|
|
14711
14649
|
});
|
|
14712
14650
|
const tuiMode = opts.tuiMode ?? tuiModeFromArgv(context.config.pi.args);
|
|
14713
14651
|
process.env["PI_SKIP_VERSION_CHECK"] ??= "1";
|
|
@@ -14737,6 +14675,7 @@ ${choices.join("\n")}`,
|
|
|
14737
14675
|
runtime.bridge.setOnUiBound(null);
|
|
14738
14676
|
unwireChords();
|
|
14739
14677
|
unwireDisconnectedQuit();
|
|
14678
|
+
unwireKeyRepeat();
|
|
14740
14679
|
rpc.close();
|
|
14741
14680
|
derived?.dispose();
|
|
14742
14681
|
throw error2;
|
|
@@ -14763,6 +14702,7 @@ ${choices.join("\n")}`,
|
|
|
14763
14702
|
runtime.bridge.setOnUiBound(null);
|
|
14764
14703
|
unwireChords();
|
|
14765
14704
|
unwireDisconnectedQuit();
|
|
14705
|
+
unwireKeyRepeat();
|
|
14766
14706
|
process.off("SIGHUP", onHangUp);
|
|
14767
14707
|
process.off("SIGTERM", onHangUp);
|
|
14768
14708
|
}
|
|
@@ -18671,10 +18611,10 @@ async function showCmd(client, args) {
|
|
|
18671
18611
|
if (t.piSettings && Object.keys(t.piSettings).length > 0) {
|
|
18672
18612
|
info(`Pi settings ${describePiSettings(flatPiSettings(t.piSettings))}`);
|
|
18673
18613
|
}
|
|
18674
|
-
const
|
|
18675
|
-
if (
|
|
18614
|
+
const secrets = await client.listSecrets("template", t.id).catch(() => null);
|
|
18615
|
+
if (secrets && secrets.secrets.length > 0) {
|
|
18676
18616
|
out("");
|
|
18677
|
-
info(`template secrets: ${
|
|
18617
|
+
info(`template secrets: ${secrets.secrets.map((s) => s.name).join(", ")}`);
|
|
18678
18618
|
}
|
|
18679
18619
|
return 0;
|
|
18680
18620
|
}
|
|
@@ -18684,9 +18624,9 @@ function describePiSettings(settings) {
|
|
|
18684
18624
|
const packageCount = Array.isArray(packages) ? packages.length : 0;
|
|
18685
18625
|
return `${names.join(", ") || "empty"}${packageCount > 0 ? ` (${packageCount} package(s))` : ""}`;
|
|
18686
18626
|
}
|
|
18687
|
-
async function writeTemplateSnapshot(
|
|
18627
|
+
async function writeTemplateSnapshot(write) {
|
|
18688
18628
|
try {
|
|
18689
|
-
return await
|
|
18629
|
+
return await write();
|
|
18690
18630
|
} catch (error2) {
|
|
18691
18631
|
if (error2 instanceof PiPodError && error2.status === 400 && /(?:unrecognized|unknown|unexpected)[^\n]*piSettings|piSettings[^\n]*(?:unrecognized|unknown|unexpected)/i.test(error2.message)) {
|
|
18692
18632
|
throw new PiPodError("the pi pod server does not support template-owned Pi settings", {
|