demobite 1.18.2 → 1.18.4

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.
@@ -89,11 +89,11 @@ const printStatus = () => {
89
89
  const cfgThere = lock?.cwd ? readJson(path.join(lock.cwd, ".recorder", "config.json")) : null;
90
90
  const st = { base: cfgThere?.base ?? null, workspace: cfgThere?.workspace?.name ?? null, ...(readJson(STATE) ?? {}) };
91
91
  if (live) {
92
- console.log(` ● Listening as "${st?.name ?? os.hostname()}" since ${new Date(lock.since).toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })} · for ${st?.workspace ?? "your workspace"} on ${(st?.base ?? "").replace(/^https?:\/\//, "") || "app.demobites.com"} (pid ${lock.pid}${lock.service ? ", login service" : ", a Terminal window"})`);
92
+ console.log(` ● Connected as "${st?.name ?? os.hostname().replace(/\.local$/, "")}" since ${new Date(lock.since).toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })} · for ${st?.workspace ?? "your workspace"} on ${(st?.base ?? "").replace(/^https?:\/\//, "") || "app.demobites.com"} (pid ${lock.pid}${lock.service ? ", login service" : ", a Terminal window"})`);
93
93
  if (st?.at) console.log(` last check-in ${ago(st.at)} · ${st.busy ? `recording: ${st.job ?? ""}` : "idle"}${st.last_error ? ` · last error: ${st.last_error}` : ""}`);
94
94
  console.log(` working in ${lock.cwd.replace(os.homedir(), "~")}${lock.repo ? ` · code: ${lock.repo}` : ""}`);
95
95
  } else {
96
- console.log(` ○ Not listening.${st?.at ? ` Last check-in ${ago(st.at)}.` : ""}`);
96
+ console.log(` ○ Not connected.${st?.at ? ` Last check-in ${ago(st.at)}.` : ""}`);
97
97
  }
98
98
  console.log(` login service: ${serviceInstalled() ? "installed (starts at login, restarts if it dies)" : "not installed"} · run: demobite ${live ? "off" : "on"}`);
99
99
  console.log(` log: ${LOG.replace(os.homedir(), "~")}`);
@@ -272,7 +272,7 @@ if (arg === "on") {
272
272
  const base = (flag("--base") || cfgAt()?.base || "https://app.demobites.com").replace(/\/+$/, "");
273
273
  const code = flag("--connect");
274
274
  if (code || !cfgAt()?.api_key || (flag("--base") && cfgAt()?.base && cfgAt().base.replace(/\/+$/, "") !== base)) {
275
- console.log("\n Connecting this machine to your DemoBites account…\n");
275
+ console.log("");
276
276
  const r = spawnSync(process.execPath, [path.join(dest, "scripts", "login.mjs"), base, ...(code ? ["--connect", code] : [])], { stdio: "inherit", cwd });
277
277
  if (r.status !== 0) process.exit(r.status ?? 1);
278
278
  }
@@ -336,9 +336,25 @@ if (arg === "on") {
336
336
  // a moment for the first check-in, then the truth
337
337
  const until = Date.now() + 8000;
338
338
  while (Date.now() < until) { const l = readJson(LOCK); if (l?.pid && pidAlive(l.pid) && l.service) break; spawnSync(process.execPath, ["-e", "setTimeout(()=>{},400)"]); }
339
- console.log("");
340
- printStatus();
341
- console.log("\n It starts with the machine from now on. No window to keep open.\n");
339
+ // The closing box (founder 2026-10-16: "something not frightening"): done, close this, it runs by itself.
340
+ console.log(`
341
+ ┌──────────────────────────────────────────────────────────┐
342
+ │ │
343
+ │ ✓ Done. DemoBites is connected on this ${process.platform === "darwin" ? "Mac" : "machine"}.${process.platform === "darwin" ? " " : " "}│
344
+ │ │
345
+ │ You can close this window. The recorder keeps │
346
+ │ running quietly in the background, and starts │
347
+ │ again with your ${process.platform === "darwin" ? "Mac" : "machine"}. Nothing to keep open.${process.platform === "darwin" ? " " : " "}│
348
+ │ │
349
+ │ Back in the app, the Recorder box says Connected. │
350
+ │ │
351
+ │ Any time, in a Terminal: │
352
+ │ demobite status is it connected │
353
+ │ demobite off stop it and remove it │
354
+ │ demobite on bring it back │
355
+ │ │
356
+ └──────────────────────────────────────────────────────────┘
357
+ `);
342
358
  process.exit(0);
343
359
  }
344
360
 
@@ -369,7 +385,7 @@ if (arg === "listen") {
369
385
  {
370
386
  const cur = readJson(LOCK);
371
387
  if (cur?.pid && cur.pid !== process.pid && pidAlive(cur.pid)) {
372
- console.log(` ● Already listening (pid ${cur.pid}, since ${new Date(cur.since).toLocaleTimeString()}${cur.service ? ", the login service" : ""}). Nothing to do.\n demobite status · demobite off`);
388
+ console.log(` ● Already connected (pid ${cur.pid}, since ${new Date(cur.since).toLocaleTimeString()}${cur.service ? ", the login service" : ""}). Nothing to do.\n demobite status · demobite off`);
373
389
  process.exit(0);
374
390
  }
375
391
  // the browsers of a listener that died hard
@@ -441,11 +457,11 @@ if (arg === "listen") {
441
457
  const event = (job_id, kind, payload) => api({ action: "event", job_id, kind, payload });
442
458
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
443
459
 
444
- console.log(`\n Listening as "${name}" for ${cfg.workspace?.name ?? "your workspace"} on ${base.replace(/^https?:\/\//, "")}.`);
460
+ console.log(`\n Connected as "${name}" for ${cfg.workspace?.name ?? "your workspace"} on ${base.replace(/^https?:\/\//, "")}.`);
445
461
  console.log(` Ask for a demo in the app (Create → Agentic Capture). ${engine === "claude" ? (agentBin ? `Claude Code${agentInfo.agent_version ? " " + agentInfo.agent_version : ""} plans the demos, on your own plan; they film on ${JOB_MODEL} unless you pick another model in the app.` : "! --engine=claude, but Claude Code was not found on this machine.") : `Demos are planned by the DemoBites agent on ${JOB_MODEL} (pick another model in the app)${process.env.DEMOBITES_NO_CODE ? "; code sharing is off" : repo ? "; the files it reads from this project are logged in each take" : ""}.`}`);
446
462
  // The one thing the person must know, boxed (founder 2026-10-15): this
447
463
  // window is the machine's side of every recording. The login service has no window: one line instead.
448
- if (SERVICE) console.log(` ${new Date().toISOString()} listening (login service, pid ${process.pid})`);
464
+ if (SERVICE) console.log(` ${new Date().toISOString()} connected (login service, pid ${process.pid})`);
449
465
  else console.log(`
450
466
  ┌──────────────────────────────────────────────────────────┐
451
467
  │ │
@@ -466,7 +482,7 @@ if (arg === "listen") {
466
482
  const s = Math.floor((Date.now() - startedAt) / 1000);
467
483
  const clock = `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`;
468
484
  const dot = s % 2 ? "●" : "○";
469
- process.stdout.write(`\r\x1b[2K ${dot} Listening · ${clock} · waiting for a demo from the app`);
485
+ process.stdout.write(`\r\x1b[2K ${dot} Connected · ${clock} · waiting for a demo from the app`);
470
486
  };
471
487
  const waitOn = () => { if (tty && !ticker) { tick(); ticker = setInterval(tick, 1000); } };
472
488
  const waitOff = () => { if (ticker) { clearInterval(ticker); ticker = null; process.stdout.write("\r\x1b[2K"); } };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "demobite",
3
- "version": "1.18.2",
3
+ "version": "1.18.4",
4
4
  "description": "The DemoBites agentic recorder — you prompt, it films a real browser, and DemoBites turns the take into an editable demo bite.",
5
5
  "bin": {
6
6
  "demobite": "launcher/index.mjs"
@@ -161,7 +161,7 @@ while (Date.now() < deadline) {
161
161
  : data.workspace;
162
162
  const canManage = cfg.scopes.includes("manage");
163
163
  console.log(`Approved. Recorder key saved to .recorder/config.json (workspace: ${wsName}).`);
164
- console.log(canManage
164
+ if (!connectCode) console.log(canManage
165
165
  ? "This key can also MANAGE your centers — wire your agent with: npx demobite mcp"
166
166
  : "This key records only. Managing by agent (MCP) comes with the Grow plan.");
167
167
  process.exit(0);
@@ -218,7 +218,7 @@ const plan = async () => {
218
218
  const res = await relay(messages);
219
219
  const content = Array.isArray(res.content) ? res.content : [];
220
220
  messages.push({ role: "assistant", content });
221
- for (const b of content) if (b.type === "text" && b.text?.trim()) console.log(` ${b.text.trim().split("\n")[0].slice(0, 140)}`);
221
+ for (const b of content) if (b.type === "text" && b.text?.trim()) say(b.text.trim().split("\n")[0].replace(/^[#*\-\s]+/, "").slice(0, 140));
222
222
  const uses = content.filter((b) => b.type === "tool_use");
223
223
  if (!uses.length) {
224
224
  if (storyboard) return;
@@ -232,13 +232,15 @@ const plan = async () => {
232
232
  const input = u.input || {};
233
233
  let out;
234
234
  try {
235
- if (u.name === "list_files") out = listFiles();
236
- else if (u.name === "read_file") out = readFile(input.path);
237
- else if (u.name === "harvest_words") { console.log(` harvesting ${(input.urls || []).join(", ")}`); out = harvest(input.urls); }
238
- else if (u.name === "explore") out = explore(input.from, input.looking_for, input.depth);
239
- else if (u.name === "screenshot") { console.log(` looking at ${input.url}`); out = screenshot(input.url); }
240
- else if (u.name === "probe") { console.log(" dry run…"); out = probe(input.storyboard); }
235
+ const where = (u) => { try { const x = new URL(u); return (x.pathname === "/" ? x.host : x.pathname).slice(0, 60); } catch { return String(u || "").slice(0, 60); } };
236
+ if (u.name === "list_files") { say("Listing the project's files…"); out = listFiles(); }
237
+ else if (u.name === "read_file") { say(`Reading ${String(input.path || "").slice(0, 80)}…`); out = readFile(input.path); }
238
+ else if (u.name === "harvest_words") { say(`Reading the app's own words on ${(input.urls || []).length} page${(input.urls || []).length === 1 ? "" : "s"}…`); out = harvest(input.urls); }
239
+ else if (u.name === "explore") { say(`Walking the app from ${where(input.from)}, looking for ${String(input.looking_for || "the brief's words").slice(0, 60)}…`); out = explore(input.from, input.looking_for, input.depth); }
240
+ else if (u.name === "screenshot") { say(`Looking at ${where(input.url)}…`); out = screenshot(input.url); }
241
+ else if (u.name === "probe") { say(`Dry run of the storyboard, ${(input.storyboard?.steps || []).length} steps…`); out = probe(input.storyboard); }
241
242
  else if (u.name === "submit_storyboard") {
243
+ say("Handing in the storyboard…");
242
244
  const bad = validate(input.storyboard);
243
245
  if (bad) out = `Not accepted: ${bad}`;
244
246
  else { storyboard = input.storyboard; creates = typeof input.creates === "string" ? input.creates : ""; out = "Accepted."; }