demobite 1.9.1 → 1.10.1
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 +30 -6
- package/package.json +1 -1
- package/skill/scripts/login.mjs +51 -37
package/launcher/index.mjs
CHANGED
|
@@ -190,9 +190,12 @@ if (arg === "listen") {
|
|
|
190
190
|
const baseFlag = argvRest[argvRest.indexOf("--base") + 1];
|
|
191
191
|
let cfg = readCfg();
|
|
192
192
|
const base = (argvRest.includes("--base") && baseFlag ? baseFlag : cfg?.base || "https://app.demobites.com").replace(/\/+$/, "");
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
// --connect <code>: the line the Agentic Capture chat gave — already approved
|
|
194
|
+
// for the signed-in person; connects this machine without a page or a click.
|
|
195
|
+
const connectCode = argvRest.includes("--connect") ? (argvRest[argvRest.indexOf("--connect") + 1] || "") : "";
|
|
196
|
+
if (connectCode || !cfg?.api_key || (argvRest.includes("--base") && cfg?.base && cfg.base.replace(/\/+$/, "") !== base)) {
|
|
197
|
+
console.log(`\n Connecting this ${connectCode ? "machine" : "project"} to your DemoBites account first…\n`);
|
|
198
|
+
const r = spawnSync("node", [path.join(dest, "scripts", "login.mjs"), base, ...(connectCode ? ["--connect", connectCode] : [])], { stdio: "inherit", cwd: process.cwd() });
|
|
196
199
|
if (r.status !== 0) process.exit(r.status ?? 1);
|
|
197
200
|
cfg = readCfg();
|
|
198
201
|
}
|
|
@@ -207,10 +210,31 @@ if (arg === "listen") {
|
|
|
207
210
|
}
|
|
208
211
|
const name = (argvRest[argvRest.indexOf("--name") + 1] && argvRest.includes("--name")) ? argvRest[argvRest.indexOf("--name") + 1] : os.hostname().replace(/\.local$/, "");
|
|
209
212
|
const agentBin = (() => { const r = spawnSync(process.platform === "win32" ? "where" : "which", ["claude"], { encoding: "utf8" }); return r.status === 0 ? r.stdout.trim().split("\n")[0] : null; })();
|
|
213
|
+
// What the agent is (version) and how it is set (the person's model and
|
|
214
|
+
// effort in ~/.claude/settings.json) — the rail shows it, the chat agent can
|
|
215
|
+
// speak to it. The model that FILMS is ours (JOB_MODEL), passed per job: the
|
|
216
|
+
// output quality is DemoBites' to defend, never a hidden knob on the machine.
|
|
217
|
+
const JOB_MODEL = process.env.DEMOBITES_JOB_MODEL || "sonnet";
|
|
218
|
+
const agentInfo = (() => {
|
|
219
|
+
if (!agentBin) return { agent: null, agent_version: null, model: null, effort: null, job_model: null };
|
|
220
|
+
const v = spawnSync(agentBin, ["--version"], { encoding: "utf8", timeout: 8000 });
|
|
221
|
+
const version = v.status === 0 ? (v.stdout.trim().match(/\d+\.\d+(?:\.\d+)?/) || [null])[0] : null;
|
|
222
|
+
let settings = {};
|
|
223
|
+
try { settings = JSON.parse(fs.readFileSync(path.join(os.homedir(), ".claude", "settings.json"), "utf8")); } catch { /* defaults */ }
|
|
224
|
+
return { agent: "Claude Code", agent_version: version, model: typeof settings.model === "string" ? settings.model : null, effort: typeof settings.effortLevel === "string" ? settings.effortLevel : (typeof settings.effort === "string" ? settings.effort : null), job_model: JOB_MODEL };
|
|
225
|
+
})();
|
|
226
|
+
// The repo this listener was started in (the Code signal on the rail): the
|
|
227
|
+
// git remote's owner/name, or null when the folder is not a repo.
|
|
228
|
+
const repo = (() => {
|
|
229
|
+
const r = spawnSync("git", ["config", "--get", "remote.origin.url"], { encoding: "utf8", cwd: process.cwd() });
|
|
230
|
+
if (r.status !== 0) return null;
|
|
231
|
+
const m = r.stdout.trim().match(/[:/]([^/:]+\/[^/]+?)(?:\.git)?$/);
|
|
232
|
+
return m ? m[1] : null;
|
|
233
|
+
})();
|
|
210
234
|
const readiness = () => {
|
|
211
235
|
let signins = [];
|
|
212
236
|
try { signins = Object.keys(JSON.parse(fs.readFileSync(path.join(process.cwd(), ".recorder", "signins.json"), "utf8")).origins || {}); } catch { /* none */ }
|
|
213
|
-
return {
|
|
237
|
+
return { ...agentInfo, chrome: haveChrome, recorder: pkg.version, signins, cwd: process.cwd(), platform: process.platform, repo };
|
|
214
238
|
};
|
|
215
239
|
const api = async (body) => {
|
|
216
240
|
try {
|
|
@@ -223,7 +247,7 @@ if (arg === "listen") {
|
|
|
223
247
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
224
248
|
|
|
225
249
|
console.log(`\n Listening as "${name}" for ${cfg.workspace?.name ?? "your workspace"} on ${base.replace(/^https?:\/\//, "")}.`);
|
|
226
|
-
console.log(` Ask for a demo in the app (Create → Agentic Capture). ${agentBin ?
|
|
250
|
+
console.log(` Ask for a demo in the app (Create → Agentic Capture). ${agentBin ? `Claude Code${agentInfo.agent_version ? " " + agentInfo.agent_version : ""} found; demos film on ${JOB_MODEL}.` : "! Claude Code was not found on this machine — install it and sign in, it does the thinking."}`);
|
|
227
251
|
console.log(" Leave this terminal open; closing it stops the listening.\n");
|
|
228
252
|
|
|
229
253
|
const runJob = async (job) => {
|
|
@@ -236,7 +260,7 @@ if (arg === "listen") {
|
|
|
236
260
|
job.url ? `Address to start from: ${job.url}` : "Address: work it out from this project (the saved config, the codebase).",
|
|
237
261
|
`Show the storyboard before filming: ${job.review_storyboard ? "yes — run storyboard.mjs await and obey its answer" : "no — storyboard.mjs await returns film at once"}.`,
|
|
238
262
|
].join("\n");
|
|
239
|
-
const child = (await import("node:child_process")).spawn(agentBin, ["-p", prompt, "--output-format", "stream-json", "--verbose", "--dangerously-skip-permissions"], {
|
|
263
|
+
const child = (await import("node:child_process")).spawn(agentBin, ["-p", prompt, "--model", JOB_MODEL, "--output-format", "stream-json", "--verbose", "--dangerously-skip-permissions"], {
|
|
240
264
|
cwd: process.cwd(),
|
|
241
265
|
env: { ...process.env, RECORDER_JOB: job.id, RECORDER_BASE: base, RECORDER_LISTEN: "1" },
|
|
242
266
|
stdio: ["ignore", "pipe", "pipe"],
|
package/package.json
CHANGED
package/skill/scripts/login.mjs
CHANGED
|
@@ -27,7 +27,8 @@ let cfgParseFailed = false;
|
|
|
27
27
|
try { cfg = JSON.parse(fs.readFileSync(cfgPath, "utf8")); } catch (e) {
|
|
28
28
|
cfgParseFailed = fs.existsSync(cfgPath);
|
|
29
29
|
}
|
|
30
|
-
const
|
|
30
|
+
const connectIdx = process.argv.indexOf("--connect");
|
|
31
|
+
const argv = process.argv.slice(2).filter((a, i) => a !== "--logout" && a !== "--connect" && i + 2 !== connectIdx + 1);
|
|
31
32
|
const wantLogout = process.argv.includes("--logout");
|
|
32
33
|
// For LOGOUT the key's own home wins: the key was minted on cfg.base, so a
|
|
33
34
|
// CLI base argument must not point the revoke at a different host (review
|
|
@@ -75,43 +76,56 @@ if (wantLogout) {
|
|
|
75
76
|
|
|
76
77
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
console.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
79
|
+
// --connect <code> (Agentic Capture chat, 2026-10-15): the app already approved
|
|
80
|
+
// this code for the signed-in person, so there is no page to open and nothing
|
|
81
|
+
// to click — poll straight away and the key arrives.
|
|
82
|
+
const connectAt = process.argv.indexOf("--connect");
|
|
83
|
+
const connectCode = connectAt >= 0 ? (process.argv[connectAt + 1] || "").trim() : "";
|
|
84
|
+
let device_code, expires_in, interval;
|
|
85
|
+
if (connectCode) {
|
|
86
|
+
device_code = connectCode; expires_in = 600; interval = 2;
|
|
87
|
+
console.log("Connecting this machine to your DemoBites account…");
|
|
88
|
+
} else {
|
|
89
|
+
let start;
|
|
90
|
+
try {
|
|
91
|
+
start = await fetch(`${base}/api/recorder/device`, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers: { "Content-Type": "application/json" },
|
|
94
|
+
body: "{}",
|
|
95
|
+
});
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.error(`Could not reach ${base}: ${e.message}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
if (!start.ok) {
|
|
101
|
+
console.error(`Device link start failed: ${start.status} ${await start.text().catch(() => "")}`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const started = await start.json();
|
|
105
|
+
device_code = started.device_code; expires_in = started.expires_in; interval = started.interval;
|
|
106
|
+
const { user_code, verification_url } = started;
|
|
107
|
+
if (!device_code || !user_code || !verification_url) {
|
|
108
|
+
console.error("Device link response missing fields.");
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
const verifyUrl = new URL(verification_url, base).toString();
|
|
112
|
+
console.log("");
|
|
113
|
+
console.log(` Open: ${verifyUrl}`);
|
|
114
|
+
console.log(` Code: ${user_code}`);
|
|
115
|
+
console.log("");
|
|
116
|
+
console.log("Approve the link in your browser. If a sign-in page appears first,");
|
|
117
|
+
console.log("sign in — the approval page follows with the same code.");
|
|
118
|
+
// A real pause before the page appears (founder 2026-10-14: "we'll open a
|
|
119
|
+
// browser now to connect, in 3 seconds"): the human is told, then has three
|
|
120
|
+
// counted seconds, then the page opens. Never silent, never a question.
|
|
121
|
+
process.stdout.write("Opening the approval page in your browser in 3");
|
|
122
|
+
for (const n of [2, 1]) { await new Promise((r) => setTimeout(r, 1000)); process.stdout.write(`… ${n}`); }
|
|
123
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
124
|
+
console.log("… now. Waiting for the approval...");
|
|
125
|
+
if (process.platform === "darwin") spawnSync("open", [verifyUrl], { stdio: "ignore" });
|
|
126
|
+
else if (process.platform === "win32") spawnSync("cmd", ["/c", "start", "", verifyUrl], { stdio: "ignore" });
|
|
127
|
+
else spawnSync("xdg-open", [verifyUrl], { stdio: "ignore" });
|
|
97
128
|
}
|
|
98
|
-
const verifyUrl = new URL(verification_url, base).toString();
|
|
99
|
-
console.log("");
|
|
100
|
-
console.log(` Open: ${verifyUrl}`);
|
|
101
|
-
console.log(` Code: ${user_code}`);
|
|
102
|
-
console.log("");
|
|
103
|
-
console.log("Approve the link in your browser. If a sign-in page appears first,");
|
|
104
|
-
console.log("sign in — the approval page follows with the same code.");
|
|
105
|
-
// A real pause before the page appears (founder 2026-10-14: "we'll open a
|
|
106
|
-
// browser now to connect, in 3 seconds"): the human is told, then has three
|
|
107
|
-
// counted seconds, then the page opens. Never silent, never a question.
|
|
108
|
-
process.stdout.write("Opening the approval page in your browser in 3");
|
|
109
|
-
for (const n of [2, 1]) { await new Promise((r) => setTimeout(r, 1000)); process.stdout.write(`… ${n}`); }
|
|
110
|
-
await new Promise((r) => setTimeout(r, 1000));
|
|
111
|
-
console.log("… now. Waiting for the approval...");
|
|
112
|
-
if (process.platform === "darwin") spawnSync("open", [verifyUrl], { stdio: "ignore" });
|
|
113
|
-
else if (process.platform === "win32") spawnSync("cmd", ["/c", "start", "", verifyUrl], { stdio: "ignore" });
|
|
114
|
-
else spawnSync("xdg-open", [verifyUrl], { stdio: "ignore" });
|
|
115
129
|
|
|
116
130
|
const deadline = Date.now() + (expires_in ?? 900) * 1000;
|
|
117
131
|
const waitMs = Math.max(2, interval ?? 5) * 1000;
|