demobite 1.18.1 → 1.18.3
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/launcher/index.mjs
CHANGED
|
@@ -89,7 +89,7 @@ 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(` ● Listening 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 {
|
|
@@ -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("
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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 listening 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 listening │
|
|
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
|
|
|
@@ -480,6 +496,8 @@ if (arg === "listen") {
|
|
|
480
496
|
console.log(`\n ${isLook ? "◉ Looking at" : "⚿ Sign-in at"} ${job.url}`);
|
|
481
497
|
noteState({ busy: true, job: job.id });
|
|
482
498
|
await event(job.id, "started", { machine: name });
|
|
499
|
+
// Alive while the person signs in (a window can wait minutes): the heartbeat, or the rail calls this machine offline.
|
|
500
|
+
const heartbeat = setInterval(() => { void api({ action: "checkin", machine_id: listen.machine_id, name, readiness: readiness(), busy: true }); }, 5000);
|
|
483
501
|
const { spawn } = await import("node:child_process");
|
|
484
502
|
const child = spawn(process.execPath, [script, job.url], { cwd: process.cwd(), env: { ...process.env, RECORDER_JOB: job.id, RECORDER_BASE: base, RECORDER_LISTEN: "1" }, stdio: ["ignore", "pipe", "pipe"], detached: process.platform !== "win32" });
|
|
485
503
|
track(child);
|
|
@@ -498,6 +516,7 @@ if (arg === "listen") {
|
|
|
498
516
|
});
|
|
499
517
|
child.stderr.on("data", (d) => { const t = d.toString().trim(); if (t) console.log(` ! ${t.split("\n")[0].slice(0, 160)}`); });
|
|
500
518
|
const code = await new Promise((r) => child.on("close", r));
|
|
519
|
+
clearInterval(heartbeat);
|
|
501
520
|
if (isLook) {
|
|
502
521
|
if (verdict) await event(job.id, "looked", verdict);
|
|
503
522
|
else await event(job.id, "failed", { message: "The look did not finish" });
|
package/package.json
CHANGED
package/scripts/record.mjs
CHANGED
|
@@ -207,7 +207,7 @@ const launchOpts = {
|
|
|
207
207
|
viewport: VIEW,
|
|
208
208
|
recordVideo: { dir: DIR, size: VIEW },
|
|
209
209
|
deviceScaleFactor: 1,
|
|
210
|
-
args: ["--force-color-profile=srgb", "--disable-blink-features=AutomationControlled"],
|
|
210
|
+
args: ["--force-color-profile=srgb", "--disable-blink-features=AutomationControlled", "--disable-features=MediaRouter,DialMediaRouteProvider", "--disable-component-update", "--no-default-browser-check"],
|
|
211
211
|
};
|
|
212
212
|
|
|
213
213
|
// THREE ways to get a browser. ONE script serves both lanes — the cloud
|
|
@@ -29,7 +29,7 @@ const DANGER = /\b(delete|remove|pay|purchase|buy|subscribe|upgrade|publish|send
|
|
|
29
29
|
|
|
30
30
|
const { chromium } = await import("playwright");
|
|
31
31
|
const profileDir = process.env.RECORDER_PROFILE || path.resolve(process.cwd(), ".recorder", "profile");
|
|
32
|
-
const launchOpts = { headless: true, viewport: { width: 1440, height: 900 }, args: ["--force-color-profile=srgb", "--disable-blink-features=AutomationControlled"], ignoreDefaultArgs: ["--enable-automation"] };
|
|
32
|
+
const launchOpts = { headless: true, viewport: { width: 1440, height: 900 }, args: ["--force-color-profile=srgb", "--disable-blink-features=AutomationControlled", "--disable-features=MediaRouter,DialMediaRouteProvider", "--disable-component-update", "--no-default-browser-check"], ignoreDefaultArgs: ["--enable-automation"] };
|
|
33
33
|
let ctx;
|
|
34
34
|
try { ctx = await chromium.launchPersistentContext(profileDir, { ...launchOpts, channel: "chrome" }); }
|
|
35
35
|
catch { ctx = await chromium.launchPersistentContext(profileDir, launchOpts); }
|
package/skill/scripts/login.mjs
CHANGED
|
@@ -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);
|
package/skill/scripts/look.mjs
CHANGED
|
@@ -27,7 +27,7 @@ const profileDir = process.env.RECORDER_PROFILE || path.resolve(process.cwd(), "
|
|
|
27
27
|
const launchOpts = {
|
|
28
28
|
headless: true,
|
|
29
29
|
viewport: { width: 1440, height: 900 },
|
|
30
|
-
args: ["--force-color-profile=srgb", "--disable-blink-features=AutomationControlled"],
|
|
30
|
+
args: ["--force-color-profile=srgb", "--disable-blink-features=AutomationControlled", "--disable-features=MediaRouter,DialMediaRouteProvider", "--disable-component-update", "--no-default-browser-check"],
|
|
31
31
|
ignoreDefaultArgs: ["--enable-automation"],
|
|
32
32
|
};
|
|
33
33
|
let ctx;
|
package/skill/scripts/plan.mjs
CHANGED
|
@@ -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())
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
else if (u.name === "
|
|
238
|
-
else if (u.name === "
|
|
239
|
-
else if (u.name === "
|
|
240
|
-
else if (u.name === "
|
|
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."; }
|
package/skill/scripts/signin.mjs
CHANGED
|
@@ -49,7 +49,7 @@ const launchOpts = {
|
|
|
49
49
|
headless: false,
|
|
50
50
|
viewport: { width: 1440, height: 900 },
|
|
51
51
|
deviceScaleFactor: 1,
|
|
52
|
-
args: ["--force-color-profile=srgb", "--disable-blink-features=AutomationControlled"],
|
|
52
|
+
args: ["--force-color-profile=srgb", "--disable-blink-features=AutomationControlled", "--disable-features=MediaRouter,DialMediaRouteProvider", "--disable-component-update", "--no-default-browser-check"],
|
|
53
53
|
ignoreDefaultArgs: ["--enable-automation"],
|
|
54
54
|
};
|
|
55
55
|
let ctx;
|