pubblue 0.6.1 → 0.6.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/dist/index.js CHANGED
@@ -6,32 +6,28 @@ import {
6
6
  TEXT_FILE_EXTENSIONS,
7
7
  buildBridgeProcessEnv,
8
8
  buildDaemonForkStdio,
9
- createApiClient,
10
9
  ensureNodeDatachannelAvailable,
10
+ errorMessage,
11
11
  failCli,
12
12
  formatApiError,
13
13
  generateMessageId,
14
14
  getAgentSocketPath,
15
- getConfig,
16
15
  getFollowReadDelayMs,
17
16
  getMimeType,
18
- getTelegramMiniAppUrl,
19
17
  ipcCall,
20
18
  isDaemonRunning,
21
19
  liveInfoPath,
22
20
  liveLogPath,
23
- loadConfig,
24
21
  messageContainsPong,
25
22
  parsePositiveIntegerOption,
26
23
  readLogTail,
27
24
  resolveActiveSlug,
28
25
  resolveBridgeMode,
29
- saveConfig,
30
26
  stopOtherDaemons,
31
27
  toCliFailure,
32
28
  waitForDaemonReady,
33
29
  writeLatestCliVersion
34
- } from "./chunk-BBJOOZHS.js";
30
+ } from "./chunk-AZQD654L.js";
35
31
 
36
32
  // src/program.ts
37
33
  import { Command } from "commander";
@@ -39,11 +35,71 @@ import { Command } from "commander";
39
35
  // src/commands/configure.ts
40
36
  import { createInterface } from "readline/promises";
41
37
 
42
- // src/commands/shared.ts
38
+ // src/lib/config.ts
43
39
  import * as fs from "fs";
40
+ import * as os from "os";
44
41
  import * as path from "path";
45
- function createClient() {
46
- const config = getConfig();
42
+ var DEFAULT_BASE_URL = "https://silent-guanaco-514.convex.site";
43
+ function getConfigDir(homeDir) {
44
+ const home = homeDir || os.homedir();
45
+ return path.join(home, ".config", "pubblue");
46
+ }
47
+ function getConfigPath(homeDir) {
48
+ const dir = getConfigDir(homeDir);
49
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
50
+ try {
51
+ fs.chmodSync(dir, 448);
52
+ } catch {
53
+ }
54
+ return path.join(dir, "config.json");
55
+ }
56
+ function readConfig(homeDir) {
57
+ const configPath = getConfigPath(homeDir);
58
+ if (!fs.existsSync(configPath)) return null;
59
+ const raw = fs.readFileSync(configPath, "utf-8");
60
+ return JSON.parse(raw);
61
+ }
62
+ function saveConfig(config, homeDir) {
63
+ const configPath = getConfigPath(homeDir);
64
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
65
+ `, {
66
+ mode: 384
67
+ });
68
+ try {
69
+ fs.chmodSync(configPath, 384);
70
+ } catch {
71
+ }
72
+ }
73
+ function getConfig(homeDir) {
74
+ const envKey = process.env.PUBBLUE_API_KEY;
75
+ const envUrl = process.env.PUBBLUE_URL;
76
+ const baseUrl = envUrl || DEFAULT_BASE_URL;
77
+ const saved = readConfig(homeDir);
78
+ if (envKey) {
79
+ return { apiKey: envKey, baseUrl, bridge: saved?.bridge };
80
+ }
81
+ if (!saved) {
82
+ throw new Error(
83
+ "Not configured. Run `pubblue configure` or set PUBBLUE_API_KEY environment variable."
84
+ );
85
+ }
86
+ return {
87
+ apiKey: saved.apiKey,
88
+ baseUrl,
89
+ bridge: saved.bridge
90
+ };
91
+ }
92
+ function getTelegramMiniAppUrl(slug) {
93
+ const saved = readConfig();
94
+ if (!saved?.telegram?.botUsername) return null;
95
+ return `https://t.me/${saved.telegram.botUsername}?startapp=${slug}`;
96
+ }
97
+
98
+ // src/commands/shared.ts
99
+ import * as fs2 from "fs";
100
+ import * as path2 from "path";
101
+ function createClient(configOverride) {
102
+ const config = configOverride || getConfig();
47
103
  return new PubApiClient(config.baseUrl, config.apiKey);
48
104
  }
49
105
  async function readFromStdin() {
@@ -65,13 +121,13 @@ function resolveVisibilityFlags(opts) {
65
121
  return void 0;
66
122
  }
67
123
  function readFile(filePath) {
68
- const resolved = path.resolve(filePath);
69
- if (!fs.existsSync(resolved)) {
124
+ const resolved = path2.resolve(filePath);
125
+ if (!fs2.existsSync(resolved)) {
70
126
  failCli(`File not found: ${resolved}`);
71
127
  }
72
128
  return {
73
- content: fs.readFileSync(resolved, "utf-8"),
74
- basename: path.basename(resolved)
129
+ content: fs2.readFileSync(resolved, "utf-8"),
130
+ basename: path2.basename(resolved)
75
131
  };
76
132
  }
77
133
 
@@ -133,103 +189,78 @@ function parsePositiveInteger(raw, key) {
133
189
  }
134
190
  return parsed;
135
191
  }
136
- var SUPPORTED_KEYS = [
137
- "openclaw.path",
138
- "openclaw.sessionId",
139
- "openclaw.threadId",
140
- "openclaw.canvasReminderEvery",
141
- "openclaw.deliver",
142
- "openclaw.deliverChannel",
143
- "openclaw.replyTo",
144
- "openclaw.deliverTimeoutMs",
145
- "openclaw.attachmentDir",
146
- "openclaw.attachmentMaxBytes",
147
- "telegram.botToken"
148
- ];
192
+ var CONFIG_KEY_REGISTRY = {
193
+ "openclaw.path": { target: "bridge", field: "openclawPath", type: "string" },
194
+ "openclaw.stateDir": { target: "bridge", field: "openclawStateDir", type: "string" },
195
+ "openclaw.sessionId": { target: "bridge", field: "sessionId", type: "string" },
196
+ "openclaw.threadId": { target: "bridge", field: "threadId", type: "string" },
197
+ "openclaw.canvasReminderEvery": {
198
+ target: "bridge",
199
+ field: "canvasReminderEvery",
200
+ type: "integer"
201
+ },
202
+ "openclaw.deliver": { target: "bridge", field: "deliver", type: "boolean" },
203
+ "openclaw.deliverChannel": { target: "bridge", field: "deliverChannel", type: "string" },
204
+ "openclaw.replyTo": { target: "bridge", field: "replyTo", type: "string" },
205
+ "openclaw.deliverTimeoutMs": { target: "bridge", field: "deliverTimeoutMs", type: "integer" },
206
+ "openclaw.attachmentDir": { target: "bridge", field: "attachmentDir", type: "string" },
207
+ "openclaw.attachmentMaxBytes": { target: "bridge", field: "attachmentMaxBytes", type: "integer" },
208
+ "claude-code.path": { target: "bridge", field: "claudeCodePath", type: "string" },
209
+ "claude-code.model": { target: "bridge", field: "claudeCodeModel", type: "string" },
210
+ "claude-code.allowedTools": { target: "bridge", field: "claudeCodeAllowedTools", type: "string" },
211
+ "claude-code.appendSystemPrompt": {
212
+ target: "bridge",
213
+ field: "claudeCodeAppendSystemPrompt",
214
+ type: "string",
215
+ displayAs: "set-only"
216
+ },
217
+ "claude-code.maxTurns": { target: "bridge", field: "claudeCodeMaxTurns", type: "integer" },
218
+ "claude-code.cwd": { target: "bridge", field: "claudeCodeCwd", type: "string" },
219
+ "telegram.botToken": {
220
+ target: "telegram",
221
+ field: "botToken",
222
+ type: "string",
223
+ cascadeUnset: ["botUsername", "hasMainWebApp"]
224
+ }
225
+ };
226
+ var SUPPORTED_KEYS = Object.keys(CONFIG_KEY_REGISTRY);
227
+ function coerceValue(raw, type, key) {
228
+ if (type === "integer") return parsePositiveInteger(raw, key);
229
+ if (type === "boolean") return parseBooleanValue(raw, key);
230
+ return raw;
231
+ }
149
232
  function applyConfigSet(bridge, telegram, key, value) {
150
- switch (key) {
151
- case "openclaw.path":
152
- bridge.openclawPath = value;
153
- return;
154
- case "openclaw.sessionId":
155
- bridge.sessionId = value;
156
- return;
157
- case "openclaw.threadId":
158
- bridge.threadId = value;
159
- return;
160
- case "openclaw.canvasReminderEvery":
161
- bridge.canvasReminderEvery = parsePositiveInteger(value, key);
162
- return;
163
- case "openclaw.deliver":
164
- bridge.deliver = parseBooleanValue(value, key);
165
- return;
166
- case "openclaw.deliverChannel":
167
- bridge.deliverChannel = value;
168
- return;
169
- case "openclaw.replyTo":
170
- bridge.replyTo = value;
171
- return;
172
- case "openclaw.deliverTimeoutMs":
173
- bridge.deliverTimeoutMs = parsePositiveInteger(value, key);
174
- return;
175
- case "openclaw.attachmentDir":
176
- bridge.attachmentDir = value;
177
- return;
178
- case "openclaw.attachmentMaxBytes":
179
- bridge.attachmentMaxBytes = parsePositiveInteger(value, key);
180
- return;
181
- case "telegram.botToken":
182
- telegram.botToken = value;
183
- return;
184
- default:
185
- throw new Error(
186
- [
187
- `Unknown config key: ${key}`,
188
- "Supported keys:",
189
- ...SUPPORTED_KEYS.map((k) => ` ${k}`)
190
- ].join("\n")
191
- );
233
+ const def = CONFIG_KEY_REGISTRY[key];
234
+ if (!def) {
235
+ throw new Error(
236
+ [
237
+ `Unknown config key: ${key}`,
238
+ "Supported keys:",
239
+ ...SUPPORTED_KEYS.map((k) => ` ${k}`)
240
+ ].join("\n")
241
+ );
242
+ }
243
+ const coerced = coerceValue(value, def.type, key);
244
+ if (def.target === "bridge") {
245
+ Object.assign(bridge, { [def.field]: coerced });
246
+ } else {
247
+ Object.assign(telegram, { [def.field]: coerced });
192
248
  }
193
249
  }
194
250
  function applyConfigUnset(bridge, telegram, key) {
195
- switch (key) {
196
- case "openclaw.path":
197
- delete bridge.openclawPath;
198
- return;
199
- case "openclaw.sessionId":
200
- delete bridge.sessionId;
201
- return;
202
- case "openclaw.threadId":
203
- delete bridge.threadId;
204
- return;
205
- case "openclaw.canvasReminderEvery":
206
- delete bridge.canvasReminderEvery;
207
- return;
208
- case "openclaw.deliver":
209
- delete bridge.deliver;
210
- return;
211
- case "openclaw.deliverChannel":
212
- delete bridge.deliverChannel;
213
- return;
214
- case "openclaw.replyTo":
215
- delete bridge.replyTo;
216
- return;
217
- case "openclaw.deliverTimeoutMs":
218
- delete bridge.deliverTimeoutMs;
219
- return;
220
- case "openclaw.attachmentDir":
221
- delete bridge.attachmentDir;
222
- return;
223
- case "openclaw.attachmentMaxBytes":
224
- delete bridge.attachmentMaxBytes;
225
- return;
226
- case "telegram.botToken":
227
- delete telegram.botToken;
228
- delete telegram.botUsername;
229
- delete telegram.hasMainWebApp;
230
- return;
231
- default:
232
- throw new Error(`Unknown config key for --unset: ${key}`);
251
+ const def = CONFIG_KEY_REGISTRY[key];
252
+ if (!def) {
253
+ throw new Error(`Unknown config key for --unset: ${key}`);
254
+ }
255
+ if (def.target === "bridge") {
256
+ delete bridge[def.field];
257
+ } else {
258
+ delete telegram[def.field];
259
+ if (def.cascadeUnset) {
260
+ for (const cascadeField of def.cascadeUnset) {
261
+ delete telegram[cascadeField];
262
+ }
263
+ }
233
264
  }
234
265
  }
235
266
  function hasValues(obj) {
@@ -261,41 +292,32 @@ async function telegramSetMenuButton(token, button) {
261
292
  throw new Error(data.description ?? "setChatMenuButton failed");
262
293
  }
263
294
  }
264
- function printConfigSummary(saved) {
265
- if (!saved) {
266
- console.log("Saved config: none");
295
+ function formatFieldValue(value, def) {
296
+ if (def.displayAs === "set-only") return "(set)";
297
+ if (def.type === "boolean") return value ? "true" : "false";
298
+ return String(value);
299
+ }
300
+ function printBridgeConfig(bridge) {
301
+ if (!hasValues(bridge)) {
302
+ console.log(" bridge: none");
267
303
  return;
268
304
  }
269
- console.log("Saved config:");
270
- console.log(` apiKey: ${maskSecret(saved.apiKey)}`);
271
- if (saved.bridge && hasValues(saved.bridge)) {
272
- if (saved.bridge.openclawPath) console.log(` openclaw.path: ${saved.bridge.openclawPath}`);
273
- if (saved.bridge.sessionId) console.log(` openclaw.sessionId: ${saved.bridge.sessionId}`);
274
- if (saved.bridge.threadId) console.log(` openclaw.threadId: ${saved.bridge.threadId}`);
275
- if (saved.bridge.canvasReminderEvery !== void 0)
276
- console.log(` openclaw.canvasReminderEvery: ${saved.bridge.canvasReminderEvery}`);
277
- if (saved.bridge.deliver !== void 0)
278
- console.log(` openclaw.deliver: ${saved.bridge.deliver ? "true" : "false"}`);
279
- if (saved.bridge.deliverChannel)
280
- console.log(` openclaw.deliverChannel: ${saved.bridge.deliverChannel}`);
281
- if (saved.bridge.replyTo) console.log(` openclaw.replyTo: ${saved.bridge.replyTo}`);
282
- if (saved.bridge.deliverTimeoutMs !== void 0)
283
- console.log(` openclaw.deliverTimeoutMs: ${saved.bridge.deliverTimeoutMs}`);
284
- if (saved.bridge.attachmentDir)
285
- console.log(` openclaw.attachmentDir: ${saved.bridge.attachmentDir}`);
286
- if (saved.bridge.attachmentMaxBytes !== void 0)
287
- console.log(` openclaw.attachmentMaxBytes: ${saved.bridge.attachmentMaxBytes}`);
288
- } else {
289
- console.log(" bridge: none");
305
+ for (const [key, def] of Object.entries(CONFIG_KEY_REGISTRY)) {
306
+ if (def.target !== "bridge") continue;
307
+ const value = bridge[def.field];
308
+ if (value === void 0) continue;
309
+ console.log(` ${key}: ${formatFieldValue(value, def)}`);
290
310
  }
291
- if (saved.telegram?.botToken && saved.telegram.botUsername) {
292
- console.log(` telegram.botToken: ${maskSecret(saved.telegram.botToken)}`);
293
- console.log(` telegram.botUsername: @${saved.telegram.botUsername}`);
294
- if (!saved.telegram.hasMainWebApp) {
311
+ }
312
+ function printTelegramConfig(telegram) {
313
+ if (telegram?.botToken && telegram.botUsername) {
314
+ console.log(` telegram.botToken: ${maskSecret(telegram.botToken)}`);
315
+ console.log(` telegram.botUsername: @${telegram.botUsername}`);
316
+ if (!telegram.hasMainWebApp) {
295
317
  console.log(" INFO: Register Mini App in @BotFather for deep links to open in Telegram");
296
318
  }
297
- } else if (saved.telegram?.botToken) {
298
- console.log(` telegram.botToken: ${maskSecret(saved.telegram.botToken)}`);
319
+ } else if (telegram?.botToken) {
320
+ console.log(` telegram.botToken: ${maskSecret(telegram.botToken)}`);
299
321
  console.log(" telegram.botUsername: (not resolved)");
300
322
  } else {
301
323
  console.log(" telegram: not configured");
@@ -303,6 +325,16 @@ function printConfigSummary(saved) {
303
325
  console.log(" Example: pubblue configure --set telegram.botToken=<BOT_TOKEN>");
304
326
  }
305
327
  }
328
+ function printConfigSummary(saved) {
329
+ if (!saved) {
330
+ console.log("Saved config: none");
331
+ return;
332
+ }
333
+ console.log("Saved config:");
334
+ console.log(` apiKey: ${maskSecret(saved.apiKey)}`);
335
+ printBridgeConfig(saved.bridge ?? {});
336
+ printTelegramConfig(saved.telegram);
337
+ }
306
338
  function registerConfigureCommand(program2) {
307
339
  program2.command("configure").description("Configure the CLI with your API key").option("--api-key <key>", "Your API key (less secure: appears in shell history)").option("--api-key-stdin", "Read API key from stdin").option(
308
340
  "--set <key=value>",
@@ -311,7 +343,7 @@ function registerConfigureCommand(program2) {
311
343
  []
312
344
  ).option("--unset <key>", "Unset config key (repeatable)", collectValues, []).option("--show", "Show saved configuration").action(
313
345
  async (opts) => {
314
- const saved = loadConfig();
346
+ const saved = readConfig();
315
347
  const hasApiUpdate = Boolean(opts.apiKey || opts.apiKeyStdin);
316
348
  const hasSet = opts.set.length > 0;
317
349
  const hasUnset = opts.unset.length > 0;
@@ -349,7 +381,7 @@ function registerConfigureCommand(program2) {
349
381
  console.log("Telegram menu button reset to default.");
350
382
  } catch (error) {
351
383
  console.error(
352
- `Warning: failed to reset Telegram menu button: ${error instanceof Error ? error.message : String(error)}`
384
+ `Warning: failed to reset Telegram menu button: ${errorMessage(error)}`
353
385
  );
354
386
  }
355
387
  }
@@ -389,21 +421,21 @@ function registerConfigureCommand(program2) {
389
421
  }
390
422
 
391
423
  // src/commands/live.ts
392
- import * as fs2 from "fs";
393
- import * as path2 from "path";
424
+ import * as fs3 from "fs";
425
+ import * as path3 from "path";
394
426
 
395
427
  // package.json
396
428
  var package_default = {
397
429
  name: "pubblue",
398
- version: "0.6.1",
399
- description: "CLI tool for publishing content and running interactive sessions via pub.blue",
430
+ version: "0.6.8",
431
+ description: "CLI tool for publishing content and running live sessions via pub.blue",
400
432
  type: "module",
401
433
  bin: {
402
434
  pubblue: "./dist/index.js"
403
435
  },
404
436
  scripts: {
405
- build: "tsup src/index.ts src/tunnel-daemon-entry.ts --format esm --dts --clean",
406
- dev: "tsup src/index.ts src/tunnel-daemon-entry.ts --format esm --watch",
437
+ build: "tsup src/index.ts src/live-daemon-entry.ts --format esm --dts --clean",
438
+ dev: "tsup src/index.ts src/live-daemon-entry.ts --format esm --watch",
407
439
  test: "vitest run",
408
440
  "test:watch": "vitest",
409
441
  lint: "tsc --noEmit"
@@ -455,39 +487,19 @@ function registerLiveCommands(program2) {
455
487
  registerDoctorCommand(program2);
456
488
  }
457
489
  function registerStartCommand(program2) {
458
- program2.command("start").description("Start the agent daemon (registers presence, awaits live requests)").requiredOption("--agent-name <name>", "Agent display name shown to the browser user").option("--bridge <mode>", "Bridge mode: openclaw|none").option("--foreground", "Run in foreground (don't fork)").action(async (opts) => {
490
+ program2.command("start").description("Start the agent daemon (registers presence, awaits live requests)").requiredOption("--agent-name <name>", "Agent display name shown to the browser user").option("--bridge <mode>", "Bridge mode: openclaw|claude-code").action(async (opts) => {
459
491
  await ensureNodeDatachannelAvailable();
460
492
  writeLatestCliVersion(CLI_VERSION);
461
493
  const runtimeConfig = getConfig();
462
- const apiClient = createApiClient(runtimeConfig);
463
494
  const bridgeMode = resolveBridgeMode(opts);
464
495
  const bridgeProcessEnv = buildBridgeProcessEnv(runtimeConfig.bridge);
465
496
  const socketPath = getAgentSocketPath();
466
497
  const infoPath = liveInfoPath("agent");
467
498
  const logPath = liveLogPath("agent");
468
499
  await stopOtherDaemons();
469
- if (opts.foreground) {
470
- const { startDaemon } = await import("./tunnel-daemon-BR5XKNEA.js");
471
- console.log("Agent daemon starting in foreground...");
472
- console.log("Press Ctrl+C to stop.");
473
- try {
474
- await startDaemon({
475
- cliVersion: CLI_VERSION,
476
- apiClient,
477
- socketPath,
478
- infoPath,
479
- bridgeMode,
480
- agentName: opts.agentName
481
- });
482
- } catch (error) {
483
- const message = error instanceof Error ? error.message : String(error);
484
- failCli(`Daemon failed: ${message}`);
485
- }
486
- return;
487
- }
488
500
  const { fork } = await import("child_process");
489
- const daemonScript = path2.join(import.meta.dirname, "tunnel-daemon-entry.js");
490
- const daemonLogFd = fs2.openSync(logPath, "a");
501
+ const daemonScript = path3.join(import.meta.dirname, "live-daemon-entry.js");
502
+ const daemonLogFd = fs3.openSync(logPath, "a");
491
503
  const child = fork(daemonScript, [], {
492
504
  detached: true,
493
505
  stdio: buildDaemonForkStdio(daemonLogFd),
@@ -502,7 +514,7 @@ function registerStartCommand(program2) {
502
514
  PUBBLUE_DAEMON_BRIDGE_MODE: bridgeMode
503
515
  }
504
516
  });
505
- fs2.closeSync(daemonLogFd);
517
+ fs3.closeSync(daemonLogFd);
506
518
  if (child.connected) {
507
519
  child.disconnect();
508
520
  }
@@ -564,12 +576,13 @@ function registerStatusCommand(program2) {
564
576
  console.log(` Last error: ${response.lastError}`);
565
577
  }
566
578
  const logPath = liveLogPath("agent");
567
- if (fs2.existsSync(logPath)) {
579
+ if (fs3.existsSync(logPath)) {
568
580
  console.log(` Log: ${logPath}`);
569
581
  }
570
582
  const bridge = response.bridge;
571
583
  if (bridge) {
572
- console.log(` Bridge: openclaw (${bridge.running ? "running" : "stopped"})`);
584
+ const bridgeLabel = response.bridgeMode ?? "unknown";
585
+ console.log(` Bridge: ${bridgeLabel} (${bridge.running ? "running" : "stopped"})`);
573
586
  if (bridge.sessionId) {
574
587
  console.log(` Bridge session: ${bridge.sessionId}`);
575
588
  }
@@ -593,10 +606,10 @@ function registerWriteCommand(program2) {
593
606
  let msg;
594
607
  let binaryBase64;
595
608
  if (opts.file) {
596
- const filePath = path2.resolve(opts.file);
597
- const ext = path2.extname(filePath).toLowerCase();
598
- const bytes = fs2.readFileSync(filePath);
599
- const filename = path2.basename(filePath);
609
+ const filePath = path3.resolve(opts.file);
610
+ const ext = path3.extname(filePath).toLowerCase();
611
+ const bytes = fs3.readFileSync(filePath);
612
+ const filename = path3.basename(filePath);
600
613
  if (ext === ".html" || ext === ".htm") {
601
614
  msg = {
602
615
  id: generateMessageId(),
@@ -713,45 +726,29 @@ function registerDoctorCommand(program2) {
713
726
  const timeoutMs = timeoutSeconds * 1e3;
714
727
  const socketPath = getAgentSocketPath();
715
728
  const slug = await resolveActiveSlug();
716
- const apiClient = createApiClient();
729
+ const apiClient = createClient();
717
730
  const fail = (message) => failCli(`Doctor failed: ${message}`);
718
731
  console.log(`Doctor: ${slug}`);
719
- let statusResponse = null;
720
- try {
721
- statusResponse = await ipcCall(socketPath, {
722
- method: "status",
723
- params: {}
724
- });
725
- } catch (error) {
726
- fail(
727
- `daemon is unreachable (${error instanceof Error ? error.message : String(error)}).`
728
- );
729
- }
730
- if (!statusResponse) {
731
- fail("daemon status returned no response.");
732
- }
733
- const daemonStatus = statusResponse;
734
- if (!daemonStatus.ok) {
735
- fail(`daemon returned non-ok status: ${String(daemonStatus.error || "unknown error")}`);
736
- }
737
- if (!daemonStatus.connected) {
732
+ const statusResponse = await ipcCall(socketPath, {
733
+ method: "status",
734
+ params: {}
735
+ }).catch((error) => fail(`daemon is unreachable (${errorMessage(error)}).`));
736
+ if (!statusResponse.ok) {
737
+ fail(`daemon returned non-ok status: ${String(statusResponse.error || "unknown error")}`);
738
+ }
739
+ if (!statusResponse.connected) {
738
740
  fail("daemon is running but browser is not connected.");
739
741
  }
740
- const channelNames = Array.isArray(daemonStatus.channels) ? daemonStatus.channels.map((entry) => String(entry)) : [];
742
+ const channelNames = Array.isArray(statusResponse.channels) ? statusResponse.channels.map((entry) => String(entry)) : [];
741
743
  for (const required of [CONTROL_CHANNEL, CHANNELS.CHAT, CHANNELS.CANVAS]) {
742
744
  if (!channelNames.includes(required)) {
743
745
  fail(`required channel is missing: ${required}`);
744
746
  }
745
747
  }
746
748
  console.log("Daemon/channel check: OK");
747
- const live = await (async () => {
748
- try {
749
- return await apiClient.getLive(slug);
750
- } catch (error) {
751
- fail(`failed to fetch live info from API: ${formatApiError(error)}`);
752
- }
753
- throw new Error("unreachable");
754
- })();
749
+ const live = await apiClient.getLive(slug).catch(
750
+ (error) => fail(`failed to fetch live info from API: ${formatApiError(error)}`)
751
+ );
755
752
  if (live.status !== "active") {
756
753
  fail(`API reports live is not active (status: ${live.status})`);
757
754
  }
@@ -932,7 +929,7 @@ function registerPubCommands(program2) {
932
929
  });
933
930
  program2.command("delete").description("Delete a pub").argument("<slug>", "Slug of the pub to delete").action(async (slug) => {
934
931
  const client = createClient();
935
- await client.remove(slug);
932
+ await client.deletePub(slug);
936
933
  console.log(`Deleted: ${slug}`);
937
934
  });
938
935
  }