pi-web-ui 0.6.2 → 0.8.0

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.
@@ -192,6 +192,28 @@ function brokenSpawnHelper() {
192
192
  }
193
193
  return "";
194
194
  }
195
+ // ---------------------------------------------------------------------------
196
+ // macOS TCC camera/mic warning (launchd-spawned servers)
197
+ // ---------------------------------------------------------------------------
198
+ // TCC attributes camera/mic access to the process chain's "responsible
199
+ // process". When pi-web-ui runs as a launchd LaunchAgent (node ← launchd),
200
+ // the responsible process is node itself — a bare CLI binary with no app
201
+ // bundle / Info.plist / NSCameraUsageDescription — so TCC silently denies
202
+ // camera access (no prompt, nothing to tick in System Settings) and
203
+ // ffmpeg-style grabbers hang on frame capture. The identical command works
204
+ // from a terminal app that already holds the camera grant. Detect the
205
+ // "no GUI ancestor" case (ppid === 1 on macOS) and warn in the terminal.
206
+ const TCC_HINT = [
207
+ "\x1b[33m[提示] 本终端由后台服务(launchd)启动,macOS 隐私权限(相机/麦克风/屏幕录制等)对此类进程不可用。\x1b[0m",
208
+ "\x1b[90m · 需要隐私权限的命令会被系统静默拒绝:不弹授权窗,系统设置里也无法勾选,表现多为卡死或无输出。",
209
+ " · 这类任务请在你自己已授权的前台终端里运行。",
210
+ " · 本终端内可运行不需要隐私权限的命令(如文件处理、网络请求、远程设备流)。",
211
+ " · 若改在前台终端里运行 pi-web-ui,本提示即不再出现。\x1b[0m",
212
+ ].join("\r\n") + "\r\n";
213
+ /** True when this server was spawned by launchd (or orphaned) on macOS — no GUI app in the ancestry, so camera/mic TCC grants are unavailable. */
214
+ function launchdSpawnedOnMac() {
215
+ return process.platform === "darwin" && process.ppid === 1;
216
+ }
195
217
  /**
196
218
  * Owns one or more PTYs for a client. All output is forwarded as
197
219
  * `terminal_output` messages through the provided emit (broadcast to every
@@ -202,6 +224,7 @@ export class TerminalManager {
202
224
  emit;
203
225
  terms = new Map();
204
226
  seq = 0;
227
+ tccHintShown = false;
205
228
  constructor(emit) {
206
229
  this.emit = emit;
207
230
  }
@@ -209,7 +232,16 @@ export class TerminalManager {
209
232
  create(id, cwd, cols, rows, fallbackCwd) {
210
233
  if (this.terms.has(id))
211
234
  return;
212
- this.spawnShell(id, cwd || fallbackCwd, cols, rows, `终端 ${++this.seq}`);
235
+ if (this.spawnShell(id, cwd || fallbackCwd, cols, rows, `终端 ${++this.seq}`)) {
236
+ this.maybeEmitTccHint(id);
237
+ }
238
+ }
239
+ /** Warn about unavailable camera/mic TCC grants in a fresh terminal, once per client. */
240
+ maybeEmitTccHint(id) {
241
+ if (this.tccHintShown || !launchdSpawnedOnMac())
242
+ return;
243
+ this.tccHintShown = true;
244
+ this.writeOut(id, TCC_HINT);
213
245
  }
214
246
  /**
215
247
  * Start a shell in the command's directory and run the command in it.
@@ -247,6 +279,7 @@ export class TerminalManager {
247
279
  // (the PTY input buffer holds it until the shell is ready).
248
280
  this.writeOut(id, "\x1b[2J\x1b[3J\x1b[H");
249
281
  this.writeOut(id, `\x1b[90m> ${command}\x1b[0m \x1b[90m(${dir})\x1b[0m\r\n`);
282
+ this.maybeEmitTccHint(id);
250
283
  if (command)
251
284
  this.input(id, command + "\r");
252
285
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-ui",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
5
5
  "license": "MIT",
6
6
  "type": "module",