deus-machine 0.3.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/dist/cli.js ADDED
@@ -0,0 +1,2477 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // package.json
13
+ var package_exports = {};
14
+ __export(package_exports, {
15
+ default: () => package_default
16
+ });
17
+ var package_default;
18
+ var init_package = __esm({
19
+ "package.json"() {
20
+ package_default = {
21
+ name: "deus-machine",
22
+ version: "0.3.3",
23
+ description: "Run Deus IDE from the command line \u2014 headless server or desktop installer",
24
+ license: "MIT",
25
+ author: {
26
+ name: "Deus",
27
+ email: "hello@deusmachine.ai"
28
+ },
29
+ homepage: "https://deusmachine.ai",
30
+ repository: {
31
+ type: "git",
32
+ url: "https://github.com/zvadaadam/deus-machine",
33
+ directory: "apps/cli"
34
+ },
35
+ keywords: [
36
+ "ide",
37
+ "ai",
38
+ "coding",
39
+ "agent",
40
+ "claude",
41
+ "agentic",
42
+ "desktop",
43
+ "headless"
44
+ ],
45
+ bin: {
46
+ deus: "./bin/deus.js"
47
+ },
48
+ files: [
49
+ "bin/",
50
+ "dist/",
51
+ "bundles/"
52
+ ],
53
+ type: "module",
54
+ engines: {
55
+ node: ">=20"
56
+ },
57
+ scripts: {
58
+ build: "bunx tsx build.ts"
59
+ },
60
+ dependencies: {
61
+ "@napi-rs/canvas": "^0.1.97",
62
+ "@openai/codex": "^0.101.0",
63
+ "@openai/codex-sdk": "^0.101.0",
64
+ "@sentry/node": "^10.40.0",
65
+ "agent-browser": "^0.21.4",
66
+ "better-sqlite3": "^12.4.1",
67
+ "node-pty": "^1.0.0",
68
+ ws: "^8.19.0"
69
+ }
70
+ };
71
+ }
72
+ });
73
+
74
+ // src/cli.ts
75
+ import { parseArgs } from "node:util";
76
+
77
+ // src/start.ts
78
+ import { spawn, execSync as execSync2 } from "node:child_process";
79
+ import { existsSync as existsSync4 } from "node:fs";
80
+ import { join as join3, resolve, dirname } from "node:path";
81
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
82
+ import { homedir as homedir3 } from "node:os";
83
+ import { mkdirSync as mkdirSync2 } from "node:fs";
84
+
85
+ // ../../shared/runtime.ts
86
+ import path from "node:path";
87
+ var DEUS_APP_ID = "com.deus.app";
88
+ var DEUS_DB_FILENAME = "deus.db";
89
+ var DEUS_PREFERENCES_FILENAME = "preferences.json";
90
+ var RUNTIME_MANIFEST_VERSION = 1;
91
+ var CLI_RUNTIME_DEPENDENCIES = [
92
+ "@napi-rs/canvas",
93
+ "@openai/codex",
94
+ "@openai/codex-sdk",
95
+ "@sentry/node",
96
+ "agent-browser",
97
+ "better-sqlite3",
98
+ "node-pty",
99
+ "ws"
100
+ ];
101
+ function getPathModule(platform4) {
102
+ return platform4 === "win32" ? path.win32 : path.posix;
103
+ }
104
+ function resolveDefaultDataDir(options) {
105
+ const targetPath = getPathModule(options.platform);
106
+ if (options.platform === "darwin") {
107
+ return targetPath.join(options.homeDir, "Library", "Application Support", DEUS_APP_ID);
108
+ }
109
+ if (options.platform === "win32") {
110
+ return targetPath.join(
111
+ options.appData || targetPath.join(options.homeDir, "AppData", "Roaming"),
112
+ DEUS_APP_ID
113
+ );
114
+ }
115
+ return targetPath.join(
116
+ options.xdgDataHome || targetPath.join(options.homeDir, ".local", "share"),
117
+ "deus"
118
+ );
119
+ }
120
+ function resolveRuntimeStagePaths(projectRoot) {
121
+ const runtimeRoot = path.join(projectRoot, "dist", "runtime");
122
+ const commonRoot = path.join(runtimeRoot, "common");
123
+ const electronRoot = path.join(runtimeRoot, "electron");
124
+ return {
125
+ root: runtimeRoot,
126
+ manifest: path.join(runtimeRoot, "manifest.json"),
127
+ common: {
128
+ root: commonRoot,
129
+ backendBundle: path.join(commonRoot, "server.bundled.cjs"),
130
+ agentServerBundle: path.join(commonRoot, "agent-server.bundled.cjs")
131
+ },
132
+ electron: {
133
+ root: electronRoot,
134
+ backendBundle: path.join(electronRoot, "backend", "server.bundled.cjs"),
135
+ agentServerBundle: path.join(electronRoot, "bin", "index.bundled.cjs")
136
+ }
137
+ };
138
+ }
139
+
140
+ // ../runtime/validate.ts
141
+ import { existsSync, readFileSync, statSync } from "node:fs";
142
+ import path2 from "node:path";
143
+ import { fileURLToPath } from "node:url";
144
+ var runtimeDir = path2.dirname(fileURLToPath(import.meta.url));
145
+ var defaultProjectRoot = path2.resolve(runtimeDir, "../..");
146
+ var BUILD_RUNTIME_COMMAND = "bun run build:runtime";
147
+ function relativeFromProjectRoot(projectRoot, targetPath) {
148
+ return path2.relative(projectRoot, targetPath).split(path2.sep).join("/");
149
+ }
150
+ function createBuildRuntimeError(message) {
151
+ return new Error(`${message}
152
+ Run \`${BUILD_RUNTIME_COMMAND}\` before packaging.`);
153
+ }
154
+ function readManifest(manifestPath) {
155
+ try {
156
+ return JSON.parse(readFileSync(manifestPath, "utf8"));
157
+ } catch (error2) {
158
+ throw createBuildRuntimeError(
159
+ `Unable to read staged runtime manifest at ${manifestPath}: ${error2 instanceof Error ? error2.message : String(error2)}`
160
+ );
161
+ }
162
+ }
163
+ function assertExists(filePath, label) {
164
+ if (!existsSync(filePath)) {
165
+ throw createBuildRuntimeError(`Missing ${label}: ${filePath}`);
166
+ }
167
+ }
168
+ function assertNotStale(projectRoot, filePath, label, sourcePath, sourceLabel) {
169
+ if (statSync(filePath).mtimeMs < statSync(sourcePath).mtimeMs) {
170
+ throw createBuildRuntimeError(
171
+ `${label} is stale: ${relativeFromProjectRoot(projectRoot, filePath)} is older than ${relativeFromProjectRoot(projectRoot, sourcePath)} (${sourceLabel})`
172
+ );
173
+ }
174
+ }
175
+ function buildExpectedManifest(projectRoot) {
176
+ const stagePaths = resolveRuntimeStagePaths(projectRoot);
177
+ return {
178
+ version: RUNTIME_MANIFEST_VERSION,
179
+ appId: DEUS_APP_ID,
180
+ data: {
181
+ databaseFile: DEUS_DB_FILENAME,
182
+ preferencesFile: DEUS_PREFERENCES_FILENAME
183
+ },
184
+ bundles: {
185
+ common: {
186
+ backend: relativeFromProjectRoot(projectRoot, stagePaths.common.backendBundle),
187
+ agentServer: relativeFromProjectRoot(projectRoot, stagePaths.common.agentServerBundle)
188
+ },
189
+ electron: {
190
+ backend: relativeFromProjectRoot(projectRoot, stagePaths.electron.backendBundle),
191
+ agentServer: relativeFromProjectRoot(projectRoot, stagePaths.electron.agentServerBundle)
192
+ }
193
+ },
194
+ nodeRuntimeDependencies: CLI_RUNTIME_DEPENDENCIES
195
+ };
196
+ }
197
+ function validateRuntimeStage(options = {}) {
198
+ const log = options.log ?? console.log;
199
+ const projectRoot = options.projectRoot ?? defaultProjectRoot;
200
+ const stagePaths = resolveRuntimeStagePaths(projectRoot);
201
+ const sources = {
202
+ backendBundle: path2.join(projectRoot, "apps", "backend", "dist", "server.bundled.cjs"),
203
+ agentServerBundle: path2.join(projectRoot, "apps", "agent-server", "dist", "index.bundled.cjs")
204
+ };
205
+ assertExists(sources.backendBundle, "runtime source bundle (backend)");
206
+ assertExists(sources.agentServerBundle, "runtime source bundle (agent-server)");
207
+ assertExists(stagePaths.manifest, "staged runtime manifest");
208
+ assertExists(stagePaths.common.backendBundle, "staged common backend bundle");
209
+ assertExists(stagePaths.common.agentServerBundle, "staged common agent-server bundle");
210
+ assertExists(stagePaths.electron.backendBundle, "staged electron backend bundle");
211
+ assertExists(stagePaths.electron.agentServerBundle, "staged electron agent-server bundle");
212
+ const manifest = readManifest(stagePaths.manifest);
213
+ const expectedManifest = buildExpectedManifest(projectRoot);
214
+ if (JSON.stringify(manifest) !== JSON.stringify(expectedManifest)) {
215
+ throw createBuildRuntimeError(
216
+ `Staged runtime manifest at ${stagePaths.manifest} does not match the current runtime contract`
217
+ );
218
+ }
219
+ assertNotStale(
220
+ projectRoot,
221
+ stagePaths.common.backendBundle,
222
+ "staged common backend bundle",
223
+ sources.backendBundle,
224
+ "backend source bundle"
225
+ );
226
+ assertNotStale(
227
+ projectRoot,
228
+ stagePaths.electron.backendBundle,
229
+ "staged electron backend bundle",
230
+ sources.backendBundle,
231
+ "backend source bundle"
232
+ );
233
+ assertNotStale(
234
+ projectRoot,
235
+ stagePaths.common.agentServerBundle,
236
+ "staged common agent-server bundle",
237
+ sources.agentServerBundle,
238
+ "agent-server source bundle"
239
+ );
240
+ assertNotStale(
241
+ projectRoot,
242
+ stagePaths.electron.agentServerBundle,
243
+ "staged electron agent-server bundle",
244
+ sources.agentServerBundle,
245
+ "agent-server source bundle"
246
+ );
247
+ const latestSourceMtime = Math.max(
248
+ statSync(sources.backendBundle).mtimeMs,
249
+ statSync(sources.agentServerBundle).mtimeMs
250
+ );
251
+ if (statSync(stagePaths.manifest).mtimeMs < latestSourceMtime) {
252
+ throw createBuildRuntimeError(
253
+ `Staged runtime manifest is stale: ${relativeFromProjectRoot(projectRoot, stagePaths.manifest)} is older than the source bundles`
254
+ );
255
+ }
256
+ log(
257
+ `\u2713 Staged runtime ready for packaging (${relativeFromProjectRoot(projectRoot, stagePaths.root)})`
258
+ );
259
+ return manifest;
260
+ }
261
+ var entryPath = process.argv[1] ? path2.resolve(process.argv[1]) : null;
262
+ if (entryPath === fileURLToPath(import.meta.url)) {
263
+ try {
264
+ validateRuntimeStage();
265
+ } catch (error2) {
266
+ console.error("Runtime validation failed:", error2);
267
+ process.exit(1);
268
+ }
269
+ }
270
+
271
+ // src/ui.ts
272
+ var ESC = "\x1B[";
273
+ var RESET = `${ESC}0m`;
274
+ var noColor = !!process.env.NO_COLOR || process.argv.includes("--no-color");
275
+ function wrap(code, text) {
276
+ if (noColor) return text;
277
+ return `${ESC}${code}m${text}${RESET}`;
278
+ }
279
+ var c = {
280
+ // Modifiers
281
+ bold: (t) => wrap("1", t),
282
+ dim: (t) => wrap("2", t),
283
+ italic: (t) => wrap("3", t),
284
+ underline: (t) => wrap("4", t),
285
+ // Colors
286
+ red: (t) => wrap("31", t),
287
+ green: (t) => wrap("32", t),
288
+ yellow: (t) => wrap("33", t),
289
+ blue: (t) => wrap("34", t),
290
+ magenta: (t) => wrap("35", t),
291
+ cyan: (t) => wrap("36", t),
292
+ white: (t) => wrap("37", t),
293
+ gray: (t) => wrap("90", t),
294
+ // Bright colors
295
+ brightCyan: (t) => wrap("96", t),
296
+ brightWhite: (t) => wrap("97", t),
297
+ // Backgrounds
298
+ bgCyan: (t) => wrap("46", t),
299
+ bgBlue: (t) => wrap("44", t),
300
+ // Combo
301
+ label: (t) => wrap("1;36", t)
302
+ // bold cyan
303
+ };
304
+ function rgb(r, g, b, text) {
305
+ if (noColor) return text;
306
+ return `\x1B[38;2;${r};${g};${b}m${text}${RESET}`;
307
+ }
308
+ var isUnicode = process.platform !== "win32" || !!process.env.WT_SESSION;
309
+ var sym = {
310
+ tick: isUnicode ? "\u2713" : "\u221A",
311
+ cross: isUnicode ? "\u2717" : "\xD7",
312
+ dot: isUnicode ? "\u25CF" : "*",
313
+ dash: isUnicode ? "\u2500" : "-",
314
+ arrow: isUnicode ? "\u2192" : "->",
315
+ diamond: isUnicode ? "\u25C6" : "*",
316
+ pointer: isUnicode ? "\u276F" : ">",
317
+ info: isUnicode ? "\u2139" : "i",
318
+ warning: isUnicode ? "\u26A0" : "!",
319
+ bullet: isUnicode ? "\u2022" : "-",
320
+ // Box drawing
321
+ tl: isUnicode ? "\u256D" : "+",
322
+ tr: isUnicode ? "\u256E" : "+",
323
+ bl: isUnicode ? "\u2570" : "+",
324
+ br: isUnicode ? "\u256F" : "+",
325
+ h: isUnicode ? "\u2500" : "-",
326
+ v: isUnicode ? "\u2502" : "|"
327
+ };
328
+ var LOGO_D = ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u255A\u2550\u2550\u2550\u2550\u2550\u255D "];
329
+ var LOGO_E = ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u255D ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"];
330
+ var LOGO_U = ["\u2588\u2588\u2557 \u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "];
331
+ var LOGO_S = ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"];
332
+ var LETTERS = [LOGO_D, LOGO_E, LOGO_U, LOGO_S];
333
+ var LOGO_BLOCK = [
334
+ [167, 139, 250],
335
+ [129, 140, 248],
336
+ [96, 165, 250],
337
+ [34, 211, 238]
338
+ ];
339
+ var LOGO_INNER = [
340
+ [100, 83, 150],
341
+ [77, 84, 149],
342
+ [58, 99, 150],
343
+ [20, 127, 143]
344
+ ];
345
+ var LOGO_FLOOR = [
346
+ [60, 50, 90],
347
+ [46, 50, 89],
348
+ [35, 59, 90],
349
+ [12, 76, 86]
350
+ ];
351
+ var SHADOW_CHARS = new Set("\u2554\u2550\u2557\u2551\u255A\u255D\u256C\u2560\u2563\u2566\u2569\u2553\u2556\u2559\u255C\u2552\u2555\u2558\u255B\u250C\u2510\u2514\u2518\u252C\u2534\u251C\u2524\u253C\u2500\u2502");
352
+ function colorLogo(text, block, shadow) {
353
+ let result = "";
354
+ let buf = "";
355
+ let cur = null;
356
+ function flush() {
357
+ if (!buf) return;
358
+ if (cur === "b") result += rgb(block[0], block[1], block[2], buf);
359
+ else if (cur === "s") result += rgb(shadow[0], shadow[1], shadow[2], buf);
360
+ else result += buf;
361
+ buf = "";
362
+ }
363
+ for (const ch of text) {
364
+ const t = ch === " " ? " " : SHADOW_CHARS.has(ch) ? "s" : "b";
365
+ if (t !== cur) {
366
+ flush();
367
+ cur = t;
368
+ }
369
+ buf += ch;
370
+ }
371
+ flush();
372
+ return result;
373
+ }
374
+ var LOGO_ASCII = [
375
+ " DDDD EEEEE U U SSSS ",
376
+ " D D E U U S ",
377
+ " D D EEE U U SS ",
378
+ " D D E U U S ",
379
+ " DDDD EEEEE UUUUU SSSS "
380
+ ];
381
+ function renderLogoFrame(letterCount) {
382
+ const lines = [];
383
+ for (let row = 0; row < 6; row++) {
384
+ const shadow = row === 5 ? LOGO_FLOOR : LOGO_INNER;
385
+ let line = " ";
386
+ for (let l = 0; l < letterCount; l++) {
387
+ line += colorLogo(LETTERS[l][row], LOGO_BLOCK[l], shadow[l]);
388
+ }
389
+ lines.push(line);
390
+ }
391
+ return lines;
392
+ }
393
+ function banner(version) {
394
+ console.log("");
395
+ if (isUnicode && !noColor) {
396
+ for (const line of renderLogoFrame(4)) {
397
+ console.log(line);
398
+ }
399
+ } else {
400
+ for (const line of LOGO_ASCII) {
401
+ console.log(c.cyan(line));
402
+ }
403
+ }
404
+ console.log("");
405
+ const tag = c.dim(" Agentic Engineering");
406
+ const ver = version ? ` ${c.dim("v" + version)}` : "";
407
+ console.log(tag + ver);
408
+ console.log("");
409
+ }
410
+ async function animatedBanner(version) {
411
+ if (!isUnicode || noColor || !process.stdout.isTTY) {
412
+ banner(version);
413
+ return;
414
+ }
415
+ const out = process.stdout;
416
+ const LOGO_ROWS = 6;
417
+ const FRAME_DELAY = 80;
418
+ console.log("");
419
+ for (let i = 0; i < LOGO_ROWS; i++) {
420
+ out.write("\n");
421
+ }
422
+ for (let letterCount = 1; letterCount <= 4; letterCount++) {
423
+ out.write(`${ESC}${LOGO_ROWS}A`);
424
+ const frame = renderLogoFrame(letterCount);
425
+ for (const line of frame) {
426
+ out.write(`${ESC}2K${line}
427
+ `);
428
+ }
429
+ if (letterCount < 4) {
430
+ await sleep(FRAME_DELAY);
431
+ }
432
+ }
433
+ await sleep(100);
434
+ console.log("");
435
+ const tag = c.dim(" Agentic Engineering");
436
+ const ver = version ? ` ${c.dim("v" + version)}` : "";
437
+ console.log(tag + ver);
438
+ console.log("");
439
+ }
440
+ var SPINNER_FRAMES = isUnicode ? ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"] : ["-", "\\", "|", "/"];
441
+ var SPINNER_INTERVAL = 80;
442
+ function spinner(text) {
443
+ const stream = process.stderr;
444
+ if (!stream.isTTY) {
445
+ const write = (symbol, finalText) => stream.write(` ${symbol} ${finalText}
446
+ `);
447
+ return {
448
+ update() {
449
+ },
450
+ succeed(t) {
451
+ write(c.green(sym.tick), t ?? text);
452
+ },
453
+ fail(t) {
454
+ write(c.red(sym.cross), t ?? text);
455
+ },
456
+ warn(t) {
457
+ write(c.yellow(sym.warning), t ?? text);
458
+ },
459
+ info(t) {
460
+ write(c.blue(sym.info), t ?? text);
461
+ },
462
+ stop() {
463
+ }
464
+ };
465
+ }
466
+ let frameIdx = 0;
467
+ let currentText = text;
468
+ let stopped = false;
469
+ function render() {
470
+ if (stopped) return;
471
+ const frame = c.cyan(SPINNER_FRAMES[frameIdx % SPINNER_FRAMES.length]);
472
+ stream.write(`\r${ESC}2K ${frame} ${currentText}`);
473
+ frameIdx++;
474
+ }
475
+ const interval = setInterval(render, SPINNER_INTERVAL);
476
+ render();
477
+ function stop(symbol, finalText) {
478
+ if (stopped) return;
479
+ stopped = true;
480
+ clearInterval(interval);
481
+ stream.write(`\r${ESC}2K ${symbol} ${finalText}
482
+ `);
483
+ }
484
+ return {
485
+ update(t) {
486
+ currentText = t;
487
+ },
488
+ succeed(t) {
489
+ stop(c.green(sym.tick), t ?? currentText);
490
+ },
491
+ fail(t) {
492
+ stop(c.red(sym.cross), t ?? currentText);
493
+ },
494
+ warn(t) {
495
+ stop(c.yellow(sym.warning), t ?? currentText);
496
+ },
497
+ info(t) {
498
+ stop(c.blue(sym.info), t ?? currentText);
499
+ },
500
+ stop() {
501
+ if (stopped) return;
502
+ stopped = true;
503
+ clearInterval(interval);
504
+ stream.write(`\r${ESC}2K`);
505
+ }
506
+ };
507
+ }
508
+ function box(lines, opts) {
509
+ const { padding = 1, borderColor = c.dim, width } = opts ?? {};
510
+ const pad = " ".repeat(padding);
511
+ const stripped = lines.map(stripAnsi);
512
+ const contentWidth = width ?? Math.max(...stripped.map((l) => l.length));
513
+ const innerWidth = contentWidth + padding * 2;
514
+ const top = borderColor(` ${sym.tl}${sym.h.repeat(innerWidth)}${sym.tr}`);
515
+ const bot = borderColor(` ${sym.bl}${sym.h.repeat(innerWidth)}${sym.br}`);
516
+ const empty = borderColor(` ${sym.v}`) + " ".repeat(innerWidth) + borderColor(sym.v);
517
+ console.log(top);
518
+ console.log(empty);
519
+ for (let i = 0; i < lines.length; i++) {
520
+ const visible = stripped[i].length;
521
+ const rightPad = " ".repeat(Math.max(0, contentWidth - visible));
522
+ console.log(borderColor(` ${sym.v}`) + pad + lines[i] + rightPad + pad + borderColor(sym.v));
523
+ }
524
+ console.log(empty);
525
+ console.log(bot);
526
+ }
527
+ function divider(label, width = 44) {
528
+ if (label) {
529
+ const lineLen = Math.max(0, width - label.length - 3);
530
+ console.log(c.dim(` ${sym.h.repeat(2)} ${label} ${sym.h.repeat(lineLen)}`));
531
+ } else {
532
+ console.log(c.dim(` ${sym.h.repeat(width)}`));
533
+ }
534
+ }
535
+ function kv(label, value, labelWidth = 12) {
536
+ const paddedLabel = label.padEnd(labelWidth);
537
+ console.log(` ${c.dim(paddedLabel)} ${value}`);
538
+ }
539
+ function success(text) {
540
+ console.log(` ${c.green(sym.tick)} ${text}`);
541
+ }
542
+ function error(text) {
543
+ console.log(` ${c.red(sym.cross)} ${text}`);
544
+ }
545
+ function warn(text) {
546
+ console.log(` ${c.yellow(sym.warning)} ${text}`);
547
+ }
548
+ function info(text) {
549
+ console.log(` ${c.blue(sym.info)} ${text}`);
550
+ }
551
+ function hint(text) {
552
+ console.log(c.dim(` ${text}`));
553
+ }
554
+ function blank() {
555
+ console.log("");
556
+ }
557
+ function gradientText(text, from, to) {
558
+ if (noColor) return text;
559
+ let result = "";
560
+ for (let i = 0; i < text.length; i++) {
561
+ const t = text.length > 1 ? i / (text.length - 1) : 0;
562
+ const r = Math.round(from[0] + (to[0] - from[0]) * t);
563
+ const g = Math.round(from[1] + (to[1] - from[1]) * t);
564
+ const b = Math.round(from[2] + (to[2] - from[2]) * t);
565
+ result += rgb(r, g, b, text[i]);
566
+ }
567
+ return result;
568
+ }
569
+ function statusLine(getText, intervalMs = 1e3) {
570
+ const stream = process.stderr;
571
+ if (!stream.isTTY) {
572
+ stream.write(` ${c.dim(sym.dot)} ${getText()}
573
+ `);
574
+ return { stop() {
575
+ } };
576
+ }
577
+ let stopped = false;
578
+ let dotCount = 0;
579
+ const dots = [" ", ". ", ".. ", "..."];
580
+ function render() {
581
+ if (stopped) return;
582
+ const dot = c.dim(dots[dotCount % dots.length]);
583
+ stream.write(`\r${ESC}2K ${c.dim(sym.dot)} ${getText()}${dot}`);
584
+ dotCount++;
585
+ }
586
+ const interval = setInterval(render, intervalMs);
587
+ render();
588
+ return {
589
+ stop() {
590
+ if (stopped) return;
591
+ stopped = true;
592
+ clearInterval(interval);
593
+ stream.write(`\r${ESC}2K`);
594
+ }
595
+ };
596
+ }
597
+ var BAR_WIDTH = 30;
598
+ var BAR_FILLED = isUnicode ? "\u2588" : "#";
599
+ var BAR_EMPTY = isUnicode ? "\u2591" : ".";
600
+ function progressBar(current, total, label) {
601
+ if (!process.stderr.isTTY) return;
602
+ const pct = Math.min(1, current / total);
603
+ const filled = Math.round(pct * BAR_WIDTH);
604
+ const empty = BAR_WIDTH - filled;
605
+ const bar = c.cyan(BAR_FILLED.repeat(filled)) + c.dim(BAR_EMPTY.repeat(empty));
606
+ const pctStr = `${Math.round(pct * 100)}%`.padStart(4);
607
+ const extra = label ? ` ${c.dim(label)}` : "";
608
+ process.stderr.write(`\r${ESC}2K ${bar} ${pctStr}${extra}`);
609
+ }
610
+ function progressBarDone(text) {
611
+ if (process.stderr.isTTY) {
612
+ process.stderr.write(`\r${ESC}2K`);
613
+ }
614
+ success(text);
615
+ }
616
+ function stripAnsi(str) {
617
+ return str.replace(/\x1b\[[0-9;]*m/g, "");
618
+ }
619
+ function sleep(ms) {
620
+ return new Promise((r) => setTimeout(r, ms));
621
+ }
622
+
623
+ // src/config.ts
624
+ import {
625
+ readFileSync as readFileSync2,
626
+ writeFileSync,
627
+ existsSync as existsSync2,
628
+ unlinkSync,
629
+ renameSync,
630
+ mkdirSync,
631
+ chmodSync
632
+ } from "node:fs";
633
+ import { join } from "node:path";
634
+ import { homedir, platform } from "node:os";
635
+ import { randomBytes } from "node:crypto";
636
+ var DEFAULT_CONFIG = {
637
+ onboarding_completed: false,
638
+ auth_method: null,
639
+ relay_enabled: true
640
+ };
641
+ function getConfigDir() {
642
+ const os = platform();
643
+ if (os === "darwin") {
644
+ return join(homedir(), ".config", "deus");
645
+ }
646
+ if (os === "win32") {
647
+ return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), "deus");
648
+ }
649
+ return join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "deus");
650
+ }
651
+ function getConfigPath() {
652
+ return join(getConfigDir(), "config.json");
653
+ }
654
+ function getServerInfoPath() {
655
+ return join(getConfigDir(), "server.json");
656
+ }
657
+ function loadConfig() {
658
+ const path3 = getConfigPath();
659
+ if (!existsSync2(path3)) return { ...DEFAULT_CONFIG };
660
+ try {
661
+ const raw = readFileSync2(path3, "utf-8");
662
+ const parsed = JSON.parse(raw);
663
+ return { ...DEFAULT_CONFIG, ...parsed };
664
+ } catch {
665
+ return { ...DEFAULT_CONFIG };
666
+ }
667
+ }
668
+ function saveConfig(config) {
669
+ const dir = getConfigDir();
670
+ mkdirSync(dir, { recursive: true });
671
+ const path3 = getConfigPath();
672
+ const tmp = path3 + ".tmp." + randomBytes(4).toString("hex");
673
+ try {
674
+ writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
675
+ chmodSync(tmp, 384);
676
+ renameSync(tmp, path3);
677
+ } catch {
678
+ try {
679
+ unlinkSync(tmp);
680
+ } catch {
681
+ }
682
+ throw new Error(`Failed to save config to ${path3}`);
683
+ }
684
+ }
685
+ function hasCompletedOnboarding() {
686
+ return loadConfig().onboarding_completed;
687
+ }
688
+ function writeServerInfo(info2) {
689
+ const dir = getConfigDir();
690
+ mkdirSync(dir, { recursive: true });
691
+ const path3 = getServerInfoPath();
692
+ writeFileSync(path3, JSON.stringify(info2, null, 2) + "\n", "utf-8");
693
+ chmodSync(path3, 384);
694
+ }
695
+ function readServerInfo() {
696
+ const path3 = getServerInfoPath();
697
+ if (!existsSync2(path3)) return null;
698
+ try {
699
+ const raw = readFileSync2(path3, "utf-8");
700
+ const info2 = JSON.parse(raw);
701
+ try {
702
+ process.kill(info2.pid, 0);
703
+ } catch {
704
+ clearServerInfo();
705
+ return null;
706
+ }
707
+ return info2;
708
+ } catch {
709
+ return null;
710
+ }
711
+ }
712
+ function clearServerInfo() {
713
+ const path3 = getServerInfoPath();
714
+ try {
715
+ unlinkSync(path3);
716
+ } catch {
717
+ }
718
+ }
719
+
720
+ // src/login.ts
721
+ import { execSync, spawnSync } from "node:child_process";
722
+ import { existsSync as existsSync3 } from "node:fs";
723
+ import { join as join2 } from "node:path";
724
+ import { homedir as homedir2, platform as platform2 } from "node:os";
725
+
726
+ // src/prompt.ts
727
+ var ESC2 = "\x1B[";
728
+ async function select(opts) {
729
+ const { message, options } = opts;
730
+ const stdin = process.stdin;
731
+ if (!stdin.isTTY) {
732
+ throw new Error(
733
+ "Interactive prompts require a TTY. Run in a terminal or set ANTHROPIC_API_KEY env var."
734
+ );
735
+ }
736
+ const stream = process.stderr;
737
+ let selected = 0;
738
+ stream.write(` ${message}
739
+ `);
740
+ function render() {
741
+ if (selected >= 0) {
742
+ stream.write(`${ESC2}${options.length}A`);
743
+ }
744
+ for (let i = 0; i < options.length; i++) {
745
+ const opt = options[i];
746
+ const isActive = i === selected;
747
+ const pointer = isActive ? c.cyan(sym.pointer) : " ";
748
+ const label = isActive ? c.cyan(opt.label) : c.dim(opt.label);
749
+ const hintText = opt.hint ? ` ${c.dim(opt.hint)}` : "";
750
+ stream.write(`${ESC2}2K ${pointer} ${label}${hintText}
751
+ `);
752
+ }
753
+ }
754
+ for (let i = 0; i < options.length; i++) stream.write("\n");
755
+ render();
756
+ return new Promise((resolve2) => {
757
+ const wasRaw = stdin.isRaw;
758
+ stdin.setRawMode(true);
759
+ stdin.resume();
760
+ function cleanup() {
761
+ stdin.setRawMode(wasRaw ?? false);
762
+ stdin.pause();
763
+ stdin.removeListener("data", onData);
764
+ }
765
+ function onData(buf) {
766
+ const key = buf.toString();
767
+ if (key === "") {
768
+ cleanup();
769
+ stream.write("\n");
770
+ process.exit(130);
771
+ }
772
+ if (key === "\x1B[A" || key === "k") {
773
+ selected = (selected - 1 + options.length) % options.length;
774
+ render();
775
+ return;
776
+ }
777
+ if (key === "\x1B[B" || key === "j") {
778
+ selected = (selected + 1) % options.length;
779
+ render();
780
+ return;
781
+ }
782
+ if (key === "\r" || key === "\n") {
783
+ cleanup();
784
+ stream.write(`${ESC2}${options.length}A`);
785
+ for (let i = 0; i < options.length; i++) {
786
+ stream.write(`${ESC2}2K
787
+ `);
788
+ }
789
+ stream.write(`${ESC2}${options.length}A`);
790
+ stream.write(`${ESC2}2K ${c.green(sym.tick)} ${options[selected].label}
791
+ `);
792
+ resolve2(options[selected].value);
793
+ }
794
+ }
795
+ stdin.on("data", onData);
796
+ });
797
+ }
798
+ async function input(opts) {
799
+ const { message, placeholder, mask } = opts;
800
+ const stdin = process.stdin;
801
+ if (!stdin.isTTY) {
802
+ throw new Error(
803
+ "Interactive prompts require a TTY. Run in a terminal or set ANTHROPIC_API_KEY env var."
804
+ );
805
+ }
806
+ const stream = process.stderr;
807
+ let value = "";
808
+ function renderValue() {
809
+ if (!value) {
810
+ return placeholder ? c.dim(placeholder) : "";
811
+ }
812
+ if (mask) {
813
+ const visible = value.slice(0, 7);
814
+ const hidden = "\u2022".repeat(Math.max(0, value.length - 7));
815
+ return visible + c.dim(hidden);
816
+ }
817
+ return value;
818
+ }
819
+ stream.write(` ${message}
820
+ `);
821
+ stream.write(`${ESC2}2K ${renderValue()}`);
822
+ return new Promise((resolve2) => {
823
+ const wasRaw = stdin.isRaw;
824
+ stdin.setRawMode(true);
825
+ stdin.resume();
826
+ function cleanup() {
827
+ stdin.setRawMode(wasRaw ?? false);
828
+ stdin.pause();
829
+ stdin.removeListener("data", onData);
830
+ }
831
+ function onData(buf) {
832
+ const key = buf.toString();
833
+ if (key === "") {
834
+ cleanup();
835
+ stream.write("\n");
836
+ process.exit(130);
837
+ }
838
+ if (key === "\r" || key === "\n") {
839
+ cleanup();
840
+ stream.write(`\r${ESC2}2K ${c.green(sym.tick)} ${mask ? renderValue() : value}
841
+ `);
842
+ resolve2(value);
843
+ return;
844
+ }
845
+ if (key === "\x7F" || key === "\b") {
846
+ value = value.slice(0, -1);
847
+ stream.write(`\r${ESC2}2K ${renderValue()}`);
848
+ return;
849
+ }
850
+ if (key.charCodeAt(0) < 32) return;
851
+ value += key;
852
+ stream.write(`\r${ESC2}2K ${renderValue()}`);
853
+ }
854
+ stdin.on("data", onData);
855
+ });
856
+ }
857
+ async function confirm(opts) {
858
+ const { message, default: defaultValue = true } = opts;
859
+ const stdin = process.stdin;
860
+ if (!stdin.isTTY) {
861
+ throw new Error(
862
+ "Interactive prompts require a TTY. Run in a terminal or set ANTHROPIC_API_KEY env var."
863
+ );
864
+ }
865
+ const stream = process.stderr;
866
+ const hint2 = defaultValue ? `[${c.bold("Y")}/n]` : `[y/${c.bold("N")}]`;
867
+ stream.write(` ${message} ${hint2} `);
868
+ return new Promise((resolve2) => {
869
+ const wasRaw = stdin.isRaw;
870
+ stdin.setRawMode(true);
871
+ stdin.resume();
872
+ function cleanup() {
873
+ stdin.setRawMode(wasRaw ?? false);
874
+ stdin.pause();
875
+ stdin.removeListener("data", onData);
876
+ }
877
+ function onData(buf) {
878
+ const key = buf.toString().toLowerCase();
879
+ if (key === "") {
880
+ cleanup();
881
+ stream.write("\n");
882
+ process.exit(130);
883
+ }
884
+ if (key === "y") {
885
+ cleanup();
886
+ stream.write(`\r${ESC2}2K ${c.green(sym.tick)} ${message} ${c.dim("Yes")}
887
+ `);
888
+ resolve2(true);
889
+ return;
890
+ }
891
+ if (key === "n") {
892
+ cleanup();
893
+ stream.write(`\r${ESC2}2K ${c.dim(sym.dash)} ${message} ${c.dim("No")}
894
+ `);
895
+ resolve2(false);
896
+ return;
897
+ }
898
+ if (key === "\r" || key === "\n") {
899
+ cleanup();
900
+ const result = defaultValue;
901
+ const icon = result ? c.green(sym.tick) : c.dim(sym.dash);
902
+ const text = result ? c.dim("Yes") : c.dim("No");
903
+ stream.write(`\r${ESC2}2K ${icon} ${message} ${text}
904
+ `);
905
+ resolve2(result);
906
+ return;
907
+ }
908
+ }
909
+ stdin.on("data", onData);
910
+ });
911
+ }
912
+
913
+ // src/login.ts
914
+ function getClaudeCliCandidates() {
915
+ const candidates = [];
916
+ if (process.env.CLAUDE_CLI_PATH) {
917
+ candidates.push(process.env.CLAUDE_CLI_PATH);
918
+ }
919
+ const os = platform2();
920
+ if (os === "darwin") {
921
+ candidates.push(
922
+ "/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js",
923
+ "/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js",
924
+ join2(homedir2(), ".npm/lib/node_modules/@anthropic-ai/claude-code/cli.js")
925
+ );
926
+ } else if (os === "linux") {
927
+ candidates.push(
928
+ "/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js",
929
+ "/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js",
930
+ join2(homedir2(), ".npm/lib/node_modules/@anthropic-ai/claude-code/cli.js")
931
+ );
932
+ }
933
+ return candidates;
934
+ }
935
+ function detectClaudeCli() {
936
+ for (const candidate of getClaudeCliCandidates()) {
937
+ if (existsSync3(candidate)) {
938
+ try {
939
+ const version = execSync(`node "${candidate}" --version`, {
940
+ encoding: "utf-8",
941
+ timeout: 5e3,
942
+ stdio: ["pipe", "pipe", "pipe"]
943
+ }).trim();
944
+ return { installed: true, path: candidate, version };
945
+ } catch {
946
+ }
947
+ }
948
+ }
949
+ try {
950
+ const claudePath = platform2() === "win32" ? execSync("where claude", {
951
+ encoding: "utf-8",
952
+ timeout: 5e3,
953
+ stdio: ["pipe", "pipe", "pipe"]
954
+ }).trim().split(/\r?\n/)[0] : execSync(`${process.env.SHELL || "/bin/sh"} -lc "command -v claude"`, {
955
+ encoding: "utf-8",
956
+ timeout: 5e3,
957
+ stdio: ["pipe", "pipe", "pipe"]
958
+ }).trim();
959
+ if (claudePath) {
960
+ try {
961
+ const version = execSync(`"${claudePath}" --version`, {
962
+ encoding: "utf-8",
963
+ timeout: 5e3,
964
+ stdio: ["pipe", "pipe", "pipe"]
965
+ }).trim();
966
+ return { installed: true, path: claudePath, version };
967
+ } catch {
968
+ return { installed: true, path: claudePath };
969
+ }
970
+ }
971
+ } catch {
972
+ }
973
+ return { installed: false };
974
+ }
975
+ function isClaudeAuthenticated() {
976
+ const credPaths = [
977
+ join2(homedir2(), ".claude", ".credentials.json"),
978
+ join2(homedir2(), ".claude", "credentials.json")
979
+ ];
980
+ for (const p of credPaths) {
981
+ if (existsSync3(p)) return true;
982
+ }
983
+ return false;
984
+ }
985
+ function persistAuth(result) {
986
+ const config = loadConfig();
987
+ config.auth_method = result.method;
988
+ config.anthropic_api_key = result.method === "api_key" ? result.apiKey : void 0;
989
+ saveConfig(config);
990
+ }
991
+ async function runAuthSetup(opts) {
992
+ const config = loadConfig();
993
+ if (!opts?.force && config.auth_method) {
994
+ return {
995
+ method: config.auth_method,
996
+ apiKey: config.anthropic_api_key
997
+ };
998
+ }
999
+ if (process.env.ANTHROPIC_API_KEY) {
1000
+ success("ANTHROPIC_API_KEY detected in environment");
1001
+ blank();
1002
+ const result = { method: "env" };
1003
+ persistAuth(result);
1004
+ return result;
1005
+ }
1006
+ const cli = detectClaudeCli();
1007
+ if (cli.installed && isClaudeAuthenticated()) {
1008
+ success(`Claude Code CLI found${cli.version ? ` (${c.dim(cli.version)})` : ""}`);
1009
+ success("Authenticated");
1010
+ blank();
1011
+ hint("Ready to go! No additional setup needed.");
1012
+ blank();
1013
+ const result = { method: "claude_cli" };
1014
+ persistAuth(result);
1015
+ return result;
1016
+ }
1017
+ if (cli.installed) {
1018
+ success(`Claude Code CLI found${cli.version ? ` (${c.dim(cli.version)})` : ""}`);
1019
+ warn("Not authenticated");
1020
+ blank();
1021
+ const choice2 = await select({
1022
+ message: "How would you like to authenticate?",
1023
+ options: [
1024
+ { label: "Run claude login", value: "login", hint: "opens browser to sign in" },
1025
+ { label: "Enter API key manually", value: "api_key" },
1026
+ { label: "Skip for now", value: "skip" }
1027
+ ]
1028
+ });
1029
+ if (choice2 === "login") {
1030
+ return await runClaudeLogin(cli.path);
1031
+ }
1032
+ if (choice2 === "api_key") {
1033
+ return await promptForApiKey();
1034
+ }
1035
+ return skipAuth();
1036
+ }
1037
+ error("Claude Code CLI not found");
1038
+ blank();
1039
+ hint("Deus uses Claude Code to power AI agents.");
1040
+ blank();
1041
+ const choice = await select({
1042
+ message: "How would you like to set it up?",
1043
+ options: [
1044
+ {
1045
+ label: "Install Claude Code",
1046
+ value: "install",
1047
+ hint: "npm install -g @anthropic-ai/claude-code"
1048
+ },
1049
+ { label: "Enter API key manually", value: "api_key" },
1050
+ { label: "Skip for now", value: "skip" }
1051
+ ]
1052
+ });
1053
+ if (choice === "install") {
1054
+ return await installAndLogin();
1055
+ }
1056
+ if (choice === "api_key") {
1057
+ return await promptForApiKey();
1058
+ }
1059
+ return skipAuth();
1060
+ }
1061
+ async function runClaudeLogin(cliPath) {
1062
+ blank();
1063
+ hint("Opening browser for authentication...");
1064
+ blank();
1065
+ const result = spawnSync(cliPath, ["login"], {
1066
+ stdio: "inherit",
1067
+ timeout: 12e4
1068
+ });
1069
+ if (result.status === 0 && isClaudeAuthenticated()) {
1070
+ blank();
1071
+ success("Authenticated successfully!");
1072
+ blank();
1073
+ persistAuth({ method: "claude_cli" });
1074
+ return { method: "claude_cli" };
1075
+ }
1076
+ blank();
1077
+ warn("Authentication may not have completed.");
1078
+ hint(`You can try again later with ${c.cyan("deus login")}`);
1079
+ blank();
1080
+ return skipAuth();
1081
+ }
1082
+ async function installAndLogin() {
1083
+ blank();
1084
+ const s = spinner("Installing Claude Code CLI...");
1085
+ const result = spawnSync("npm", ["install", "-g", "@anthropic-ai/claude-code"], {
1086
+ stdio: ["pipe", "pipe", "pipe"],
1087
+ timeout: 12e4
1088
+ });
1089
+ if (result.status !== 0) {
1090
+ s.fail("Installation failed");
1091
+ blank();
1092
+ hint("Try installing manually:");
1093
+ console.log(` ${c.cyan("npm install -g @anthropic-ai/claude-code")}`);
1094
+ blank();
1095
+ return skipAuth();
1096
+ }
1097
+ s.succeed("Claude Code CLI installed");
1098
+ const cli = detectClaudeCli();
1099
+ if (cli.installed && cli.path) {
1100
+ return await runClaudeLogin(cli.path);
1101
+ }
1102
+ blank();
1103
+ warn("CLI installed but could not be found in PATH.");
1104
+ hint("Try opening a new terminal and running:");
1105
+ console.log(` ${c.cyan("claude login")}`);
1106
+ blank();
1107
+ return skipAuth();
1108
+ }
1109
+ async function promptForApiKey() {
1110
+ blank();
1111
+ hint(`Get your key from ${c.cyan(c.underline("console.anthropic.com"))}`);
1112
+ blank();
1113
+ const key = await input({
1114
+ message: "Enter your Anthropic API key:",
1115
+ mask: true
1116
+ });
1117
+ if (!key || key.trim().length < 10) {
1118
+ blank();
1119
+ warn("Invalid API key.");
1120
+ return skipAuth();
1121
+ }
1122
+ const trimmed = key.trim();
1123
+ if (!trimmed.startsWith("sk-ant-")) {
1124
+ blank();
1125
+ warn("Key doesn't look like an Anthropic API key (should start with sk-ant-)");
1126
+ hint("Saving anyway \u2014 you can change it later.");
1127
+ }
1128
+ blank();
1129
+ success("API key saved");
1130
+ blank();
1131
+ persistAuth({ method: "api_key", apiKey: trimmed });
1132
+ return { method: "api_key", apiKey: trimmed };
1133
+ }
1134
+ function skipAuth() {
1135
+ persistAuth({ method: "skipped" });
1136
+ hint(`You can configure this later with ${c.cyan("deus login")}`);
1137
+ blank();
1138
+ return { method: "skipped" };
1139
+ }
1140
+
1141
+ // src/onboarding.ts
1142
+ async function runOnboarding() {
1143
+ if (hasCompletedOnboarding()) {
1144
+ const config2 = loadConfig();
1145
+ return {
1146
+ auth: {
1147
+ method: config2.auth_method || "skipped",
1148
+ apiKey: config2.anthropic_api_key
1149
+ },
1150
+ relayEnabled: config2.relay_enabled
1151
+ };
1152
+ }
1153
+ hint("Welcome to Deus! Let's get you set up.");
1154
+ blank();
1155
+ divider("Step 1 of 2 \u2014 AI Agent");
1156
+ blank();
1157
+ const auth = await runAuthSetup({ force: true });
1158
+ divider("Step 2 of 2 \u2014 Remote Access");
1159
+ blank();
1160
+ hint("Access Deus from your phone or another");
1161
+ hint(`computer via ${c.cyan(c.underline("app.deusmachine.ai"))}`);
1162
+ blank();
1163
+ const relayEnabled = await confirm({
1164
+ message: "Enable remote access?",
1165
+ default: true
1166
+ });
1167
+ if (relayEnabled) {
1168
+ success("Remote access enabled");
1169
+ } else {
1170
+ hint("Remote access disabled. You can enable it later in Settings.");
1171
+ }
1172
+ blank();
1173
+ const config = loadConfig();
1174
+ config.onboarding_completed = true;
1175
+ config.relay_enabled = relayEnabled;
1176
+ config.installed_at = (/* @__PURE__ */ new Date()).toISOString();
1177
+ saveConfig(config);
1178
+ divider("All set!");
1179
+ blank();
1180
+ return { auth, relayEnabled };
1181
+ }
1182
+
1183
+ // src/qr.ts
1184
+ var EXP = new Uint8Array(256);
1185
+ var LOG = new Uint8Array(256);
1186
+ (() => {
1187
+ let x = 1;
1188
+ for (let i = 0; i < 255; i++) {
1189
+ EXP[i] = x;
1190
+ LOG[x] = i;
1191
+ x = x << 1 ^ (x & 128 ? 285 : 0);
1192
+ }
1193
+ EXP[255] = EXP[0];
1194
+ })();
1195
+ function gfMul(a, b) {
1196
+ if (a === 0 || b === 0) return 0;
1197
+ return EXP[(LOG[a] + LOG[b]) % 255];
1198
+ }
1199
+ function rsGeneratorPoly(n) {
1200
+ let g = new Uint8Array([1]);
1201
+ for (let i = 0; i < n; i++) {
1202
+ const next = new Uint8Array(g.length + 1);
1203
+ for (let j = 0; j < g.length; j++) {
1204
+ next[j] ^= g[j];
1205
+ next[j + 1] ^= gfMul(g[j], EXP[i]);
1206
+ }
1207
+ g = next;
1208
+ }
1209
+ return g;
1210
+ }
1211
+ function rsEncode(data, ecCount) {
1212
+ const gen = rsGeneratorPoly(ecCount);
1213
+ const result = new Uint8Array(data.length + ecCount);
1214
+ result.set(data);
1215
+ for (let i = 0; i < data.length; i++) {
1216
+ const coef = result[i];
1217
+ if (coef !== 0) {
1218
+ for (let j = 0; j < gen.length; j++) {
1219
+ result[i + j] ^= gfMul(gen[j], coef);
1220
+ }
1221
+ }
1222
+ }
1223
+ return result.slice(data.length);
1224
+ }
1225
+ var VERSION_INFO = [
1226
+ [0, 0, 0],
1227
+ // v0 placeholder
1228
+ [26, 7, 1],
1229
+ // v1: 26 total, 7 EC, 1 block → 19 data
1230
+ [44, 10, 1],
1231
+ // v2: 44 total, 10 EC → 34 data
1232
+ [70, 15, 1],
1233
+ // v3: 70 total, 15 EC → 55 data
1234
+ [100, 20, 1],
1235
+ // v4: 100 total, 20 EC → 80 data
1236
+ [134, 26, 1]
1237
+ // v5: 134 total, 26 EC → 108 data
1238
+ ];
1239
+ var ALIGNMENT_POS = [
1240
+ [],
1241
+ // v0
1242
+ [],
1243
+ // v1
1244
+ [6, 18],
1245
+ // v2
1246
+ [6, 22],
1247
+ // v3
1248
+ [6, 26],
1249
+ // v4
1250
+ [6, 30]
1251
+ // v5
1252
+ ];
1253
+ function getVersion(dataLen) {
1254
+ for (let v = 1; v <= 5; v++) {
1255
+ const [total, ec] = VERSION_INFO[v];
1256
+ const dataCapacity = total - ec;
1257
+ const overhead = v <= 9 ? 2 : 3;
1258
+ if (dataLen + overhead <= dataCapacity) return v;
1259
+ }
1260
+ throw new Error("Data too long for QR version 1-5");
1261
+ }
1262
+ function encodeData(data, version) {
1263
+ const [totalCW, ecCW] = VERSION_INFO[version];
1264
+ const dataCW = totalCW - ecCW;
1265
+ const buf = new Uint8Array(dataCW);
1266
+ let bitPos = 0;
1267
+ function writeBits(val, count) {
1268
+ for (let i = count - 1; i >= 0; i--) {
1269
+ if (val & 1 << i) {
1270
+ buf[bitPos >> 3] |= 128 >> (bitPos & 7);
1271
+ }
1272
+ bitPos++;
1273
+ }
1274
+ }
1275
+ writeBits(4, 4);
1276
+ writeBits(data.length, 8);
1277
+ for (let i = 0; i < data.length; i++) {
1278
+ writeBits(data.charCodeAt(i) & 255, 8);
1279
+ }
1280
+ writeBits(0, Math.min(4, dataCW * 8 - bitPos));
1281
+ bitPos = Math.ceil(bitPos / 8) * 8;
1282
+ let padByte = 0;
1283
+ const padPatterns = [236, 17];
1284
+ while (bitPos < dataCW * 8) {
1285
+ writeBits(padPatterns[padByte % 2], 8);
1286
+ padByte++;
1287
+ }
1288
+ return buf;
1289
+ }
1290
+ function createMatrix(version) {
1291
+ const size = 17 + version * 4;
1292
+ const matrix = [];
1293
+ for (let i = 0; i < size; i++) {
1294
+ matrix.push(new Array(size).fill(-1));
1295
+ }
1296
+ return { matrix, size };
1297
+ }
1298
+ function addFinderPattern(matrix, row, col) {
1299
+ for (let r = -1; r <= 7; r++) {
1300
+ for (let c2 = -1; c2 <= 7; c2++) {
1301
+ const mr = row + r;
1302
+ const mc = col + c2;
1303
+ if (mr < 0 || mr >= matrix.length || mc < 0 || mc >= matrix.length) continue;
1304
+ if (r === -1 || r === 7 || c2 === -1 || c2 === 7) {
1305
+ matrix[mr][mc] = 0;
1306
+ } else if (r === 0 || r === 6 || c2 === 0 || c2 === 6) {
1307
+ matrix[mr][mc] = 1;
1308
+ } else if (r >= 2 && r <= 4 && c2 >= 2 && c2 <= 4) {
1309
+ matrix[mr][mc] = 1;
1310
+ } else {
1311
+ matrix[mr][mc] = 0;
1312
+ }
1313
+ }
1314
+ }
1315
+ }
1316
+ function addAlignmentPattern(matrix, row, col) {
1317
+ for (let r = -2; r <= 2; r++) {
1318
+ for (let c2 = -2; c2 <= 2; c2++) {
1319
+ const mr = row + r;
1320
+ const mc = col + c2;
1321
+ if (matrix[mr][mc] !== -1) continue;
1322
+ if (Math.abs(r) === 2 || Math.abs(c2) === 2) {
1323
+ matrix[mr][mc] = 1;
1324
+ } else if (r === 0 && c2 === 0) {
1325
+ matrix[mr][mc] = 1;
1326
+ } else {
1327
+ matrix[mr][mc] = 0;
1328
+ }
1329
+ }
1330
+ }
1331
+ }
1332
+ function addTimingPatterns(matrix, size) {
1333
+ for (let i = 8; i < size - 8; i++) {
1334
+ if (matrix[6][i] === -1) matrix[6][i] = i % 2 === 0 ? 1 : 0;
1335
+ if (matrix[i][6] === -1) matrix[i][6] = i % 2 === 0 ? 1 : 0;
1336
+ }
1337
+ }
1338
+ function reserveFormatArea(matrix, size) {
1339
+ for (let i = 0; i <= 8; i++) {
1340
+ if (matrix[8][i] === -1) matrix[8][i] = 0;
1341
+ if (matrix[i][8] === -1) matrix[i][8] = 0;
1342
+ }
1343
+ for (let i = 0; i <= 7; i++) {
1344
+ if (matrix[8][size - 1 - i] === -1) matrix[8][size - 1 - i] = 0;
1345
+ }
1346
+ for (let i = 0; i <= 7; i++) {
1347
+ if (matrix[size - 1 - i][8] === -1) matrix[size - 1 - i][8] = 0;
1348
+ }
1349
+ matrix[size - 8][8] = 1;
1350
+ }
1351
+ function placeData(matrix, size, dataBits) {
1352
+ let bitIdx = 0;
1353
+ let upward = true;
1354
+ for (let col = size - 1; col >= 1; col -= 2) {
1355
+ if (col === 6) col = 5;
1356
+ const rows = upward ? Array.from({ length: size }, (_, i) => size - 1 - i) : Array.from({ length: size }, (_, i) => i);
1357
+ for (const row of rows) {
1358
+ for (const dc of [0, -1]) {
1359
+ const c2 = col + dc;
1360
+ if (c2 < 0 || matrix[row][c2] !== -1) continue;
1361
+ matrix[row][c2] = bitIdx < dataBits.length ? dataBits[bitIdx++] : 0;
1362
+ }
1363
+ }
1364
+ upward = !upward;
1365
+ }
1366
+ }
1367
+ var MASKS = [
1368
+ (r, c2) => (r + c2) % 2 === 0,
1369
+ (r) => r % 2 === 0,
1370
+ (_, c2) => c2 % 3 === 0,
1371
+ (r, c2) => (r + c2) % 3 === 0,
1372
+ (r, c2) => (Math.floor(r / 2) + Math.floor(c2 / 3)) % 2 === 0,
1373
+ (r, c2) => r * c2 % 2 + r * c2 % 3 === 0,
1374
+ (r, c2) => (r * c2 % 2 + r * c2 % 3) % 2 === 0,
1375
+ (r, c2) => ((r + c2) % 2 + r * c2 % 3) % 2 === 0
1376
+ ];
1377
+ function applyMask(matrix, reserved, size, maskIdx) {
1378
+ const masked = matrix.map((r) => [...r]);
1379
+ const fn = MASKS[maskIdx];
1380
+ for (let r = 0; r < size; r++) {
1381
+ for (let c2 = 0; c2 < size; c2++) {
1382
+ if (!reserved[r][c2] && fn(r, c2)) {
1383
+ masked[r][c2] ^= 1;
1384
+ }
1385
+ }
1386
+ }
1387
+ return masked;
1388
+ }
1389
+ function scoreMask(matrix, size) {
1390
+ let score = 0;
1391
+ for (let r = 0; r < size; r++) {
1392
+ let count = 1;
1393
+ for (let c2 = 1; c2 < size; c2++) {
1394
+ if (matrix[r][c2] === matrix[r][c2 - 1]) {
1395
+ count++;
1396
+ if (count === 5) score += 3;
1397
+ else if (count > 5) score += 1;
1398
+ } else {
1399
+ count = 1;
1400
+ }
1401
+ }
1402
+ }
1403
+ for (let c2 = 0; c2 < size; c2++) {
1404
+ let count = 1;
1405
+ for (let r = 1; r < size; r++) {
1406
+ if (matrix[r][c2] === matrix[r - 1][c2]) {
1407
+ count++;
1408
+ if (count === 5) score += 3;
1409
+ else if (count > 5) score += 1;
1410
+ } else {
1411
+ count = 1;
1412
+ }
1413
+ }
1414
+ }
1415
+ for (let r = 0; r < size; r++) {
1416
+ for (let c2 = 0; c2 <= size - 7; c2++) {
1417
+ if (matrix[r][c2] === 1 && matrix[r][c2 + 1] === 0 && matrix[r][c2 + 2] === 1 && matrix[r][c2 + 3] === 1 && matrix[r][c2 + 4] === 1 && matrix[r][c2 + 5] === 0 && matrix[r][c2 + 6] === 1) {
1418
+ score += 40;
1419
+ }
1420
+ }
1421
+ }
1422
+ return score;
1423
+ }
1424
+ var FORMAT_STRINGS = [30660, 29427, 32170, 30877, 26159, 25368, 27713, 26998];
1425
+ function placeFormatInfo(matrix, size, maskIdx) {
1426
+ const bits = FORMAT_STRINGS[maskIdx];
1427
+ const positions1 = [
1428
+ [8, 0],
1429
+ [8, 1],
1430
+ [8, 2],
1431
+ [8, 3],
1432
+ [8, 4],
1433
+ [8, 5],
1434
+ [8, 7],
1435
+ [8, 8],
1436
+ [7, 8],
1437
+ [5, 8],
1438
+ [4, 8],
1439
+ [3, 8],
1440
+ [2, 8],
1441
+ [1, 8],
1442
+ [0, 8]
1443
+ ];
1444
+ const positions2 = [
1445
+ [size - 1, 8],
1446
+ [size - 2, 8],
1447
+ [size - 3, 8],
1448
+ [size - 4, 8],
1449
+ [size - 5, 8],
1450
+ [size - 6, 8],
1451
+ [size - 7, 8],
1452
+ [8, size - 8],
1453
+ [8, size - 7],
1454
+ [8, size - 6],
1455
+ [8, size - 5],
1456
+ [8, size - 4],
1457
+ [8, size - 3],
1458
+ [8, size - 2],
1459
+ [8, size - 1]
1460
+ ];
1461
+ for (let i = 0; i < 15; i++) {
1462
+ const bit = bits >> i & 1;
1463
+ const [r1, c1] = positions1[i];
1464
+ matrix[r1][c1] = bit;
1465
+ const [r2, c2] = positions2[i];
1466
+ matrix[r2][c2] = bit;
1467
+ }
1468
+ }
1469
+ function generateQR(data) {
1470
+ const version = getVersion(data.length);
1471
+ const [totalCW, ecCW] = VERSION_INFO[version];
1472
+ const size = 17 + version * 4;
1473
+ const dataCodewords = encodeData(data, version);
1474
+ const ecCodewords = rsEncode(dataCodewords, ecCW);
1475
+ const allCodewords = new Uint8Array(totalCW);
1476
+ allCodewords.set(dataCodewords);
1477
+ allCodewords.set(ecCodewords, dataCodewords.length);
1478
+ const dataBits = [];
1479
+ for (const byte of allCodewords) {
1480
+ for (let bit = 7; bit >= 0; bit--) {
1481
+ dataBits.push(byte >> bit & 1);
1482
+ }
1483
+ }
1484
+ const { matrix } = createMatrix(version);
1485
+ addFinderPattern(matrix, 0, 0);
1486
+ addFinderPattern(matrix, 0, size - 7);
1487
+ addFinderPattern(matrix, size - 7, 0);
1488
+ addTimingPatterns(matrix, size);
1489
+ const alignPos = ALIGNMENT_POS[version];
1490
+ if (alignPos.length > 0) {
1491
+ for (const r of alignPos) {
1492
+ for (const c2 of alignPos) {
1493
+ if (r <= 8 && c2 <= 8) continue;
1494
+ if (r <= 8 && c2 >= size - 8) continue;
1495
+ if (r >= size - 8 && c2 <= 8) continue;
1496
+ addAlignmentPattern(matrix, r, c2);
1497
+ }
1498
+ }
1499
+ }
1500
+ reserveFormatArea(matrix, size);
1501
+ const reserved = matrix.map((r) => r.map((v) => v !== -1));
1502
+ placeData(matrix, size, dataBits);
1503
+ let bestMask = 0;
1504
+ let bestScore = Infinity;
1505
+ for (let m = 0; m < 8; m++) {
1506
+ const masked = applyMask(matrix, reserved, size, m);
1507
+ placeFormatInfo(masked, size, m);
1508
+ const score = scoreMask(masked, size);
1509
+ if (score < bestScore) {
1510
+ bestScore = score;
1511
+ bestMask = m;
1512
+ }
1513
+ }
1514
+ const final = applyMask(matrix, reserved, size, bestMask);
1515
+ placeFormatInfo(final, size, bestMask);
1516
+ const QUIET = 4;
1517
+ const qSize = size + QUIET * 2;
1518
+ const lines = [];
1519
+ for (let r = 0; r < qSize; r += 2) {
1520
+ let line = "";
1521
+ for (let c2 = 0; c2 < qSize; c2++) {
1522
+ const topR = r - QUIET;
1523
+ const botR = r - QUIET + 1;
1524
+ const col = c2 - QUIET;
1525
+ const top = topR >= 0 && topR < size && col >= 0 && col < size ? final[topR][col] : 0;
1526
+ const bot = botR >= 0 && botR < size && col >= 0 && col < size ? final[botR][col] : 0;
1527
+ if (top && bot) {
1528
+ line += "\u2588";
1529
+ } else if (top && !bot) {
1530
+ line += "\u2580";
1531
+ } else if (!top && bot) {
1532
+ line += "\u2584";
1533
+ } else {
1534
+ line += " ";
1535
+ }
1536
+ }
1537
+ lines.push(line);
1538
+ }
1539
+ return lines;
1540
+ }
1541
+ function printQR(data, indent = 4) {
1542
+ const pad = " ".repeat(indent);
1543
+ try {
1544
+ const lines = generateQR(data);
1545
+ for (const line of lines) {
1546
+ console.log(pad + line);
1547
+ }
1548
+ } catch {
1549
+ console.log(pad + "(QR code unavailable \u2014 use the link below)");
1550
+ }
1551
+ }
1552
+
1553
+ // src/lib/http.ts
1554
+ import { request } from "node:http";
1555
+ function httpGet(port, path3, timeoutMs = 3e3) {
1556
+ return new Promise((resolve2) => {
1557
+ const req = request(
1558
+ { hostname: "localhost", port, path: path3, method: "GET", timeout: timeoutMs },
1559
+ (res) => {
1560
+ let body = "";
1561
+ res.on("data", (chunk) => body += chunk.toString());
1562
+ res.on("end", () => {
1563
+ try {
1564
+ resolve2(JSON.parse(body));
1565
+ } catch {
1566
+ resolve2(null);
1567
+ }
1568
+ });
1569
+ }
1570
+ );
1571
+ req.on("error", () => resolve2(null));
1572
+ req.on("timeout", () => {
1573
+ req.destroy();
1574
+ resolve2(null);
1575
+ });
1576
+ req.end();
1577
+ });
1578
+ }
1579
+ function httpPost(port, path3, body) {
1580
+ return new Promise((resolve2, reject) => {
1581
+ const payload = body ? JSON.stringify(body) : "";
1582
+ const req = request(
1583
+ {
1584
+ hostname: "localhost",
1585
+ port,
1586
+ path: path3,
1587
+ method: "POST",
1588
+ headers: {
1589
+ "Content-Type": "application/json",
1590
+ "Content-Length": Buffer.byteLength(payload)
1591
+ },
1592
+ timeout: 5e3
1593
+ },
1594
+ (res) => {
1595
+ let data = "";
1596
+ res.on("data", (chunk) => data += chunk.toString());
1597
+ res.on("end", () => {
1598
+ if (res.statusCode !== 200) {
1599
+ reject(new Error(`${path3} returned ${res.statusCode}: ${data}`));
1600
+ return;
1601
+ }
1602
+ try {
1603
+ resolve2(JSON.parse(data));
1604
+ } catch {
1605
+ reject(new Error(`Invalid JSON from ${path3}`));
1606
+ }
1607
+ });
1608
+ }
1609
+ );
1610
+ req.on("error", reject);
1611
+ req.on("timeout", () => {
1612
+ req.destroy();
1613
+ reject(new Error(`Timeout: ${path3}`));
1614
+ });
1615
+ req.end(payload);
1616
+ });
1617
+ }
1618
+
1619
+ // src/lib/format.ts
1620
+ function formatUptime(ms) {
1621
+ const s = Math.floor(ms / 1e3);
1622
+ if (s < 60) return `${s}s`;
1623
+ const m = Math.floor(s / 60);
1624
+ if (m < 60) return `${m}m ${s % 60}s`;
1625
+ const h = Math.floor(m / 60);
1626
+ return `${h}h ${m % 60}m`;
1627
+ }
1628
+ function formatTimeAgo(isoDate) {
1629
+ const diff = Date.now() - new Date(isoDate).getTime();
1630
+ const mins = Math.floor(diff / 6e4);
1631
+ if (mins < 1) return "just now";
1632
+ if (mins < 60) return `${mins}m ago`;
1633
+ const hours = Math.floor(mins / 60);
1634
+ if (hours < 24) return `${hours}h ago`;
1635
+ const days = Math.floor(hours / 24);
1636
+ return `${days}d ago`;
1637
+ }
1638
+ function formatBytes(bytes) {
1639
+ if (bytes < 1024) return `${bytes} B`;
1640
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1641
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1642
+ }
1643
+
1644
+ // src/pair.ts
1645
+ async function showPairCode(backendPort) {
1646
+ const pairData = await httpPost(
1647
+ backendPort,
1648
+ "/api/remote-auth/generate-pair-code"
1649
+ );
1650
+ const pairingUrl = `https://app.deusmachine.ai/pair?code=${encodeURIComponent(pairData.code)}`;
1651
+ const expiresMin = Math.floor(pairData.expires_in_seconds / 60);
1652
+ blank();
1653
+ divider("Connect from anywhere");
1654
+ blank();
1655
+ hint("Scan QR code or open the link:");
1656
+ blank();
1657
+ printQR(pairingUrl, 4);
1658
+ blank();
1659
+ console.log(` ${c.cyan(c.underline(pairingUrl))}`);
1660
+ blank();
1661
+ hint("Or enter code manually:");
1662
+ blank();
1663
+ box([` ${c.bold(c.brightWhite(pairData.code))} `], {
1664
+ borderColor: c.cyan,
1665
+ width: pairData.code.length + 10
1666
+ });
1667
+ blank();
1668
+ hint(`Expires in ${expiresMin} minutes.`);
1669
+ blank();
1670
+ divider();
1671
+ }
1672
+ async function pair() {
1673
+ const serverInfo = readServerInfo();
1674
+ if (!serverInfo) {
1675
+ error("No running Deus server found.");
1676
+ blank();
1677
+ hint(`Start the server first with ${c.cyan("deus start")}`);
1678
+ blank();
1679
+ process.exit(1);
1680
+ }
1681
+ try {
1682
+ await showPairCode(serverInfo.backendPort);
1683
+ } catch (err) {
1684
+ error(`Could not generate pairing code: ${err.message}`);
1685
+ blank();
1686
+ hint("Make sure the Deus server is running.");
1687
+ blank();
1688
+ process.exit(1);
1689
+ }
1690
+ const devicesRes = await httpGet(
1691
+ serverInfo.backendPort,
1692
+ "/api/remote-auth/devices"
1693
+ );
1694
+ const devices = devicesRes?.devices || [];
1695
+ if (devices.length > 0) {
1696
+ blank();
1697
+ divider("Connected devices");
1698
+ blank();
1699
+ for (const device of devices) {
1700
+ const age = formatTimeAgo(device.last_seen_at || device.created_at);
1701
+ console.log(` ${c.green(sym.dot)} ${device.name}${c.dim(` ${age}`)}`);
1702
+ }
1703
+ blank();
1704
+ divider();
1705
+ blank();
1706
+ }
1707
+ }
1708
+
1709
+ // src/start.ts
1710
+ var __filename = fileURLToPath2(import.meta.url);
1711
+ var __dirname = dirname(__filename);
1712
+ async function start(options) {
1713
+ const { dataDir } = options;
1714
+ if (!hasCompletedOnboarding()) {
1715
+ await runOnboarding();
1716
+ blank();
1717
+ }
1718
+ const config = loadConfig();
1719
+ const paths = resolveBundlePaths();
1720
+ if (!paths) {
1721
+ error("Could not find Deus bundles.");
1722
+ blank();
1723
+ hint(`Run ${c.cyan("bun run build:cli")} from the monorepo root first.`);
1724
+ blank();
1725
+ process.exit(1);
1726
+ }
1727
+ const nodeCmd = resolveNodeBinary();
1728
+ const dbPath = resolveDataDir(dataDir);
1729
+ kv("Database", c.dim(dbPath));
1730
+ blank();
1731
+ const children = [];
1732
+ let status = null;
1733
+ let shuttingDown = false;
1734
+ let exitCode = 0;
1735
+ function shutdown(code = 0) {
1736
+ if (shuttingDown) return;
1737
+ shuttingDown = true;
1738
+ exitCode = code;
1739
+ if (status) status.stop();
1740
+ blank();
1741
+ const msg = gradientText("Shutting down...", [167, 139, 250], [34, 211, 238]);
1742
+ console.log(` ${msg}`);
1743
+ const exitPromises = [];
1744
+ for (const child of children) {
1745
+ if (child.process.exitCode === null && child.process.signalCode === null) {
1746
+ exitPromises.push(
1747
+ new Promise((resolve2) => {
1748
+ child.process.once("exit", resolve2);
1749
+ child.process.kill("SIGTERM");
1750
+ })
1751
+ );
1752
+ }
1753
+ }
1754
+ const drainTimeout = setTimeout(() => {
1755
+ for (const child of children) {
1756
+ if (child.process.exitCode === null && child.process.signalCode === null) {
1757
+ child.process.kill("SIGKILL");
1758
+ }
1759
+ }
1760
+ }, 5e3);
1761
+ Promise.all(exitPromises).finally(() => {
1762
+ clearTimeout(drainTimeout);
1763
+ clearServerInfo();
1764
+ blank();
1765
+ const bye = gradientText("Thanks for using Deus!", [167, 139, 250], [34, 211, 238]);
1766
+ console.log(` ${bye}`);
1767
+ blank();
1768
+ process.exit(exitCode);
1769
+ });
1770
+ }
1771
+ process.on("SIGINT", () => shutdown());
1772
+ process.on("SIGTERM", () => shutdown());
1773
+ const extraEnv = {};
1774
+ if (config.auth_method === "api_key" && config.anthropic_api_key) {
1775
+ extraEnv.ANTHROPIC_API_KEY = config.anthropic_api_key;
1776
+ }
1777
+ const s1 = spinner("Starting agent server...");
1778
+ const agentServerUrl = await startProcess({
1779
+ name: "agent-server",
1780
+ command: nodeCmd,
1781
+ args: [paths.agentServer],
1782
+ env: { DATABASE_PATH: dbPath, ...extraEnv },
1783
+ waitFor: /LISTEN_URL=(.+)/,
1784
+ children
1785
+ });
1786
+ if (!agentServerUrl) {
1787
+ s1.fail("Agent server failed to start");
1788
+ shutdown(1);
1789
+ return;
1790
+ }
1791
+ s1.succeed(`Agent server ${c.dim("ready")}`);
1792
+ const s2 = spinner("Starting backend...");
1793
+ const backendPort = await startProcess({
1794
+ name: "backend",
1795
+ command: nodeCmd,
1796
+ args: [paths.backend],
1797
+ env: {
1798
+ AGENT_SERVER_URL: agentServerUrl,
1799
+ DATABASE_PATH: dbPath,
1800
+ PORT: "0",
1801
+ ...extraEnv
1802
+ },
1803
+ waitFor: /\[BACKEND_PORT\](\d+)/,
1804
+ children
1805
+ });
1806
+ if (!backendPort) {
1807
+ s2.fail("Backend failed to start");
1808
+ shutdown(1);
1809
+ return;
1810
+ }
1811
+ s2.succeed(`Backend ${c.dim("ready")}`);
1812
+ const port = parseInt(backendPort, 10);
1813
+ writeServerInfo({
1814
+ pid: process.pid,
1815
+ backendPort: port,
1816
+ agentServerUrl,
1817
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
1818
+ });
1819
+ blank();
1820
+ box(
1821
+ [
1822
+ gradientText("Deus is running", [167, 139, 250], [34, 211, 238]),
1823
+ "",
1824
+ `API ${c.dim(`http://localhost:${port}`)}`
1825
+ ],
1826
+ { borderColor: c.cyan, width: 38 }
1827
+ );
1828
+ blank();
1829
+ if (config.relay_enabled) {
1830
+ await sleep(1500);
1831
+ try {
1832
+ await showPairCode(port);
1833
+ } catch {
1834
+ blank();
1835
+ warn("Could not generate pairing code.");
1836
+ hint(`Run ${c.cyan("deus pair")} to try again once the relay connects.`);
1837
+ blank();
1838
+ }
1839
+ } else {
1840
+ blank();
1841
+ hint(`Remote access is disabled. Run ${c.cyan("deus login")} to re-run setup and enable it.`);
1842
+ blank();
1843
+ }
1844
+ blank();
1845
+ const startedAt = /* @__PURE__ */ new Date();
1846
+ status = statusLine(() => {
1847
+ const elapsed = formatUptime(Date.now() - startedAt.getTime());
1848
+ return `${c.dim(`Running for ${elapsed}`)} ${c.dim(`${sym.bullet} Ctrl+C to stop`)}`;
1849
+ }, 2e3);
1850
+ for (const child of children) {
1851
+ child.process.on("exit", (code, signal) => {
1852
+ if (!shuttingDown) {
1853
+ blank();
1854
+ warn(
1855
+ `${child.name} exited unexpectedly${signal ? ` (${signal})` : code ? ` (code ${code})` : ""}`
1856
+ );
1857
+ shutdown(1);
1858
+ }
1859
+ });
1860
+ }
1861
+ await new Promise(() => {
1862
+ });
1863
+ }
1864
+ function resolveBundlePaths() {
1865
+ const cliRoot = resolve(__dirname, "..");
1866
+ const bundledDir = join3(cliRoot, "bundles");
1867
+ if (existsSync4(join3(bundledDir, "agent-server.bundled.cjs"))) {
1868
+ return {
1869
+ agentServer: join3(bundledDir, "agent-server.bundled.cjs"),
1870
+ backend: join3(bundledDir, "server.bundled.cjs")
1871
+ };
1872
+ }
1873
+ const monorepoRoot = resolve(cliRoot, "../..");
1874
+ const runtimePaths = resolveRuntimeStagePaths(monorepoRoot);
1875
+ try {
1876
+ validateRuntimeStage({ projectRoot: monorepoRoot, log: () => {
1877
+ } });
1878
+ return {
1879
+ agentServer: runtimePaths.common.agentServerBundle,
1880
+ backend: runtimePaths.common.backendBundle
1881
+ };
1882
+ } catch (error2) {
1883
+ warn(
1884
+ `Staged runtime is missing or stale in monorepo mode: ${error2 instanceof Error ? error2.message : String(error2)}`
1885
+ );
1886
+ }
1887
+ return null;
1888
+ }
1889
+ function resolveDataDir(customDir) {
1890
+ const dir = customDir ?? resolveDefaultDataDir({
1891
+ platform: process.platform,
1892
+ homeDir: homedir3(),
1893
+ appData: process.env.APPDATA,
1894
+ xdgDataHome: process.env.XDG_DATA_HOME
1895
+ });
1896
+ mkdirSync2(dir, { recursive: true });
1897
+ return join3(dir, DEUS_DB_FILENAME);
1898
+ }
1899
+ function resolveNodeBinary() {
1900
+ if (process.env.ELECTRON_RUN_AS_NODE === "1") return process.execPath;
1901
+ try {
1902
+ const electronPath = execSync2(
1903
+ `node -e "try { console.log(require('electron')) } catch { process.exit(1) }"`,
1904
+ { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
1905
+ ).trim();
1906
+ if (electronPath && existsSync4(electronPath)) {
1907
+ try {
1908
+ execSync2(
1909
+ `node -e "const D = require('better-sqlite3'); const d = new D(':memory:'); d.close()"`,
1910
+ { timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
1911
+ );
1912
+ return process.execPath;
1913
+ } catch {
1914
+ return electronPath;
1915
+ }
1916
+ }
1917
+ } catch {
1918
+ }
1919
+ return process.execPath;
1920
+ }
1921
+ async function startProcess(opts) {
1922
+ const { name, command, args, env, waitFor, children, timeoutMs = 15e3 } = opts;
1923
+ return new Promise((resolve2) => {
1924
+ const processEnv = {};
1925
+ if (command !== process.execPath && command.includes("Electron")) {
1926
+ processEnv.ELECTRON_RUN_AS_NODE = "1";
1927
+ }
1928
+ const child = spawn(command, args, {
1929
+ stdio: ["ignore", "pipe", "pipe"],
1930
+ env: { ...process.env, ...processEnv, ...env }
1931
+ });
1932
+ children.push({ process: child, name });
1933
+ let buffer = "";
1934
+ let resolved = false;
1935
+ const timeout = setTimeout(() => {
1936
+ if (!resolved) {
1937
+ resolved = true;
1938
+ child.kill("SIGTERM");
1939
+ resolve2(null);
1940
+ }
1941
+ }, timeoutMs);
1942
+ function onStdout(data) {
1943
+ buffer += data.toString();
1944
+ const match = buffer.match(waitFor);
1945
+ if (match && !resolved) {
1946
+ resolved = true;
1947
+ clearTimeout(timeout);
1948
+ child.stdout?.removeListener("data", onStdout);
1949
+ buffer = "";
1950
+ resolve2(match[1]);
1951
+ }
1952
+ }
1953
+ child.stdout?.on("data", onStdout);
1954
+ child.on("exit", () => {
1955
+ if (!resolved) {
1956
+ resolved = true;
1957
+ clearTimeout(timeout);
1958
+ resolve2(null);
1959
+ }
1960
+ });
1961
+ });
1962
+ }
1963
+
1964
+ // src/desktop.ts
1965
+ import { execSync as execSync3, spawn as spawn2 } from "node:child_process";
1966
+ import { createWriteStream, existsSync as existsSync5, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "node:fs";
1967
+ import { join as join4 } from "node:path";
1968
+ import { tmpdir, platform as platform3, arch, homedir as homedir4 } from "node:os";
1969
+ import { get } from "node:https";
1970
+ var GITHUB_REPO = "zvadaadam/deus-machine";
1971
+ var INSTALL_PATHS = {
1972
+ darwin: ["/Applications/Deus.app", `${homedir4()}/Applications/Deus.app`],
1973
+ win32: [
1974
+ `${process.env.LOCALAPPDATA || ""}\\Programs\\Deus\\Deus.exe`,
1975
+ `${process.env.PROGRAMFILES || ""}\\Deus\\Deus.exe`
1976
+ ],
1977
+ linux: [`${homedir4()}/.local/bin/Deus.AppImage`, "/usr/local/bin/Deus.AppImage"]
1978
+ };
1979
+ function hasDisplay() {
1980
+ const os = platform3();
1981
+ if (process.env.CI || process.env.DOCKER || existsSync5("/.dockerenv")) return false;
1982
+ if (process.env.SSH_CONNECTION || process.env.SSH_TTY) return false;
1983
+ if (os === "darwin") return true;
1984
+ if (os === "win32") return true;
1985
+ if (os === "linux") return !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
1986
+ return false;
1987
+ }
1988
+ function findInstalledApp() {
1989
+ const os = platform3();
1990
+ const paths = INSTALL_PATHS[os] || [];
1991
+ for (const p of paths) {
1992
+ if (p && existsSync5(p)) return p;
1993
+ }
1994
+ return null;
1995
+ }
1996
+ function launchDesktop(appPath) {
1997
+ const os = platform3();
1998
+ switch (os) {
1999
+ case "darwin":
2000
+ spawn2("open", [appPath], { detached: true, stdio: "ignore" }).unref();
2001
+ break;
2002
+ case "win32":
2003
+ case "linux":
2004
+ spawn2(appPath, [], { detached: true, stdio: "ignore" }).unref();
2005
+ break;
2006
+ }
2007
+ }
2008
+ function replaceInstalledMacApp(appPath, destPath) {
2009
+ const stagedPath = `${destPath}.staged`;
2010
+ const backupPath = `${destPath}.backup`;
2011
+ const hadExistingInstall = existsSync5(destPath);
2012
+ execSync3(`rm -rf "${stagedPath}" "${backupPath}"`, { stdio: "pipe" });
2013
+ execSync3(`ditto "${appPath}" "${stagedPath}"`, { stdio: "pipe" });
2014
+ if (hadExistingInstall) {
2015
+ execSync3(`mv "${destPath}" "${backupPath}"`, { stdio: "pipe" });
2016
+ }
2017
+ try {
2018
+ execSync3(`mv "${stagedPath}" "${destPath}"`, { stdio: "pipe" });
2019
+ if (hadExistingInstall) {
2020
+ execSync3(`rm -rf "${backupPath}"`, { stdio: "pipe" });
2021
+ }
2022
+ } catch (error2) {
2023
+ execSync3(`rm -rf "${stagedPath}"`, { stdio: "pipe" });
2024
+ if (hadExistingInstall && existsSync5(backupPath) && !existsSync5(destPath)) {
2025
+ execSync3(`mv "${backupPath}" "${destPath}"`, { stdio: "pipe" });
2026
+ }
2027
+ throw error2;
2028
+ }
2029
+ }
2030
+ async function installDesktop(options) {
2031
+ const { version } = options;
2032
+ const os = platform3();
2033
+ const cpuArch = arch();
2034
+ const assetPattern = getAssetPattern(os, cpuArch);
2035
+ if (!assetPattern) {
2036
+ error(`Unsupported platform: ${os} ${cpuArch}`);
2037
+ blank();
2038
+ hint("Supported: macOS (arm64/x64), Windows (x64), Linux (x64)");
2039
+ hint(`Or run ${c.cyan("deus start")} to run as a headless server.`);
2040
+ blank();
2041
+ process.exit(1);
2042
+ }
2043
+ kv("Platform", `${os} ${cpuArch}`, 10);
2044
+ kv("Package", c.dim(assetPattern.description), 10);
2045
+ blank();
2046
+ const s1 = spinner("Fetching latest release...");
2047
+ const release = await fetchRelease(version);
2048
+ if (!release) {
2049
+ s1.fail("Could not find a release on GitHub");
2050
+ blank();
2051
+ hint(`Repo: ${c.dim(GITHUB_REPO)}`);
2052
+ hint("Make sure the repository has published releases.");
2053
+ hint(`Or run ${c.cyan("deus start")} to run as a headless server.`);
2054
+ blank();
2055
+ process.exit(1);
2056
+ }
2057
+ s1.succeed(`Found release ${c.cyan(release.tag_name)}`);
2058
+ const asset = release.assets.find((a) => assetPattern.matcher(a.name));
2059
+ if (!asset) {
2060
+ error(`No matching installer for ${os} ${cpuArch}`);
2061
+ blank();
2062
+ hint("Available downloads:");
2063
+ for (const a of release.assets) {
2064
+ hint(` ${a.name}`);
2065
+ }
2066
+ blank();
2067
+ process.exit(1);
2068
+ }
2069
+ const downloadDir = join4(tmpdir(), "deus-installer");
2070
+ mkdirSync3(downloadDir, { recursive: true });
2071
+ const downloadPath = join4(downloadDir, asset.name);
2072
+ await downloadFile(asset.browser_download_url, downloadPath);
2073
+ const s3 = spinner("Installing...");
2074
+ const installed = await installForPlatform(os, downloadPath, s3);
2075
+ try {
2076
+ unlinkSync2(downloadPath);
2077
+ } catch {
2078
+ }
2079
+ if (installed) {
2080
+ blank();
2081
+ success("Deus is ready!");
2082
+ blank();
2083
+ }
2084
+ }
2085
+ function getAssetPattern(os, cpuArch) {
2086
+ switch (os) {
2087
+ case "darwin": {
2088
+ const archSuffix = cpuArch === "arm64" ? "arm64" : "x64";
2089
+ return {
2090
+ matcher: (name) => name.endsWith(".dmg") && name.includes(archSuffix),
2091
+ description: `macOS DMG (${archSuffix})`
2092
+ };
2093
+ }
2094
+ case "win32":
2095
+ return {
2096
+ matcher: (name) => name.endsWith(".exe") && !name.includes("blockmap"),
2097
+ description: "Windows installer (exe)"
2098
+ };
2099
+ case "linux":
2100
+ return {
2101
+ matcher: (name) => name.endsWith(".AppImage"),
2102
+ description: "Linux AppImage"
2103
+ };
2104
+ default:
2105
+ return null;
2106
+ }
2107
+ }
2108
+ async function fetchRelease(version) {
2109
+ const endpoint = version === "latest" ? `https://api.github.com/repos/${GITHUB_REPO}/releases/latest` : `https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${version}`;
2110
+ const TIMEOUT = 3e4;
2111
+ return new Promise((resolve2) => {
2112
+ const req = get(
2113
+ endpoint,
2114
+ { headers: { "User-Agent": "deus-cli", Accept: "application/vnd.github.v3+json" } },
2115
+ (res) => {
2116
+ if (res.statusCode === 302 || res.statusCode === 301) {
2117
+ const loc = res.headers.location;
2118
+ if (loc) {
2119
+ const req2 = get(loc, { headers: { "User-Agent": "deus-cli" } }, (r) => {
2120
+ collectBody(r).then((b) => {
2121
+ try {
2122
+ resolve2(JSON.parse(b));
2123
+ } catch {
2124
+ resolve2(null);
2125
+ }
2126
+ });
2127
+ });
2128
+ req2.setTimeout(TIMEOUT, () => req2.destroy());
2129
+ req2.on("error", () => resolve2(null));
2130
+ return;
2131
+ }
2132
+ }
2133
+ if (res.statusCode !== 200) {
2134
+ resolve2(null);
2135
+ return;
2136
+ }
2137
+ collectBody(res).then((b) => {
2138
+ try {
2139
+ resolve2(JSON.parse(b));
2140
+ } catch {
2141
+ resolve2(null);
2142
+ }
2143
+ });
2144
+ }
2145
+ );
2146
+ req.setTimeout(TIMEOUT, () => req.destroy());
2147
+ req.on("error", () => resolve2(null));
2148
+ });
2149
+ }
2150
+ function collectBody(res) {
2151
+ return new Promise((resolve2) => {
2152
+ let body = "";
2153
+ res.on("data", (chunk) => body += chunk.toString());
2154
+ res.on("end", () => resolve2(body));
2155
+ res.on("error", () => resolve2(""));
2156
+ });
2157
+ }
2158
+ async function downloadFile(url, dest) {
2159
+ return new Promise((resolve2, reject) => {
2160
+ const download = (downloadUrl) => {
2161
+ const req = get(downloadUrl, { headers: { "User-Agent": "deus-cli" } }, (res) => {
2162
+ if (res.statusCode === 302 || res.statusCode === 301) {
2163
+ const loc = res.headers.location;
2164
+ res.resume();
2165
+ if (loc) {
2166
+ download(loc);
2167
+ return;
2168
+ }
2169
+ }
2170
+ if (res.statusCode !== 200) {
2171
+ reject(new Error(`Download failed with status ${res.statusCode}`));
2172
+ return;
2173
+ }
2174
+ const totalSize = parseInt(res.headers["content-length"] || "0", 10);
2175
+ let downloaded = 0;
2176
+ const file = createWriteStream(dest);
2177
+ res.on("error", (err) => file.destroy(err));
2178
+ res.on("data", (chunk) => {
2179
+ downloaded += chunk.length;
2180
+ if (totalSize > 0) {
2181
+ progressBar(
2182
+ downloaded,
2183
+ totalSize,
2184
+ formatBytes(downloaded) + " / " + formatBytes(totalSize)
2185
+ );
2186
+ }
2187
+ });
2188
+ res.pipe(file);
2189
+ file.on("close", () => {
2190
+ if (!res.complete || totalSize > 0 && downloaded !== totalSize) {
2191
+ reject(new Error("Download incomplete"));
2192
+ return;
2193
+ }
2194
+ progressBarDone(`Downloaded ${c.dim(formatBytes(totalSize))}`);
2195
+ resolve2();
2196
+ });
2197
+ file.on("error", reject);
2198
+ });
2199
+ req.setTimeout(12e4, () => req.destroy(new Error("Download timed out")));
2200
+ req.on("error", reject);
2201
+ };
2202
+ download(url);
2203
+ });
2204
+ }
2205
+ async function installForPlatform(os, filePath, s) {
2206
+ switch (os) {
2207
+ case "darwin": {
2208
+ let mountPoint;
2209
+ try {
2210
+ const mountOutput = execSync3(`hdiutil attach "${filePath}" -nobrowse`, {
2211
+ encoding: "utf-8"
2212
+ });
2213
+ mountPoint = mountOutput.split("\n").filter((line) => line.includes("/Volumes/")).map((line) => line.trim().split(" ").pop()?.trim()).find(Boolean);
2214
+ if (!mountPoint) {
2215
+ s.fail("Could not mount disk image");
2216
+ return false;
2217
+ }
2218
+ const appName = "Deus.app";
2219
+ const appPath = `${mountPoint}/${appName}`;
2220
+ const destPath = `/Applications/${appName}`;
2221
+ if (existsSync5(appPath)) {
2222
+ replaceInstalledMacApp(appPath, destPath);
2223
+ s.succeed(`Installed to ${c.dim("/Applications/Deus.app")}`);
2224
+ launchDesktop(destPath);
2225
+ } else {
2226
+ s.warn("DMG mounted \u2014 drag Deus to Applications to finish");
2227
+ }
2228
+ return true;
2229
+ } catch {
2230
+ s.fail("Auto-install failed \u2014 opening DMG manually");
2231
+ execSync3(`open "${filePath}"`);
2232
+ hint("Drag Deus.app to Applications, then launch it from Applications.");
2233
+ return false;
2234
+ } finally {
2235
+ if (mountPoint) {
2236
+ try {
2237
+ execSync3(`hdiutil detach "${mountPoint}" -quiet`, { stdio: "pipe" });
2238
+ } catch {
2239
+ }
2240
+ }
2241
+ }
2242
+ }
2243
+ case "win32": {
2244
+ s.succeed("Launching installer...");
2245
+ spawn2(filePath, [], { detached: true, stdio: "ignore" }).unref();
2246
+ hint("Follow the on-screen instructions to complete installation.");
2247
+ return true;
2248
+ }
2249
+ case "linux": {
2250
+ try {
2251
+ execSync3(`chmod +x "${filePath}"`);
2252
+ const installDir = join4(homedir4(), ".local", "bin");
2253
+ mkdirSync3(installDir, { recursive: true });
2254
+ const destPath = join4(installDir, "Deus.AppImage");
2255
+ execSync3(`cp "${filePath}" "${destPath}"`);
2256
+ s.succeed(`Installed to ${c.dim(destPath)}`);
2257
+ launchDesktop(destPath);
2258
+ return true;
2259
+ } catch {
2260
+ s.fail("Installation failed \u2014 check permissions");
2261
+ return false;
2262
+ }
2263
+ }
2264
+ default:
2265
+ s.fail("Unsupported platform");
2266
+ return false;
2267
+ }
2268
+ }
2269
+
2270
+ // src/status.ts
2271
+ async function showStatus() {
2272
+ const serverInfo = readServerInfo();
2273
+ if (!serverInfo) {
2274
+ error("No running Deus server found.");
2275
+ blank();
2276
+ hint(`Start the server with ${c.cyan("deus start")}`);
2277
+ blank();
2278
+ process.exit(1);
2279
+ }
2280
+ const port = serverInfo.backendPort;
2281
+ const [health, relayStatus, devicesRes, agentAuth] = await Promise.all([
2282
+ httpGet(port, "/api/health"),
2283
+ httpGet(port, "/api/settings/relay-status"),
2284
+ httpGet(port, "/api/remote-auth/devices"),
2285
+ httpGet(port, "/api/settings/agent-auth")
2286
+ ]);
2287
+ const isRunning = health !== null;
2288
+ blank();
2289
+ divider("Server");
2290
+ blank();
2291
+ kv("Status", isRunning ? c.green("running") : c.red("not responding"), 14);
2292
+ kv("Port", String(port), 14);
2293
+ kv("PID", String(serverInfo.pid), 14);
2294
+ kv("Uptime", formatUptime(Date.now() - new Date(serverInfo.startedAt).getTime()), 14);
2295
+ blank();
2296
+ divider("Remote Access");
2297
+ blank();
2298
+ if (relayStatus) {
2299
+ kv("Relay", relayStatus.connected ? c.green("connected") : c.yellow("disconnected"), 14);
2300
+ if (relayStatus.serverId) {
2301
+ kv("Server ID", c.dim(relayStatus.serverId.slice(0, 12) + "..."), 14);
2302
+ }
2303
+ kv("Clients", String(relayStatus.clients), 14);
2304
+ } else {
2305
+ kv("Relay", c.dim("unknown"), 14);
2306
+ }
2307
+ const devices = devicesRes?.devices || [];
2308
+ if (devices.length > 0) {
2309
+ blank();
2310
+ divider("Paired Devices");
2311
+ blank();
2312
+ for (const device of devices) {
2313
+ const age = formatTimeAgo(device.last_seen_at || device.created_at);
2314
+ console.log(` ${c.green(sym.dot)} ${device.name.padEnd(24)} ${c.dim(age)}`);
2315
+ }
2316
+ }
2317
+ if (agentAuth?.agents) {
2318
+ blank();
2319
+ divider("AI Agents");
2320
+ blank();
2321
+ for (const agent of agentAuth.agents) {
2322
+ let status;
2323
+ if (!agent.initialized) {
2324
+ status = c.dim("not installed");
2325
+ } else if (agent.authenticated) {
2326
+ const email = agent.account?.email ? c.dim(` (${agent.account.email})`) : "";
2327
+ status = c.green("authenticated") + email;
2328
+ } else {
2329
+ status = c.yellow("not authenticated");
2330
+ }
2331
+ kv(agent.name, status, 14);
2332
+ }
2333
+ }
2334
+ blank();
2335
+ divider();
2336
+ blank();
2337
+ }
2338
+
2339
+ // src/cli.ts
2340
+ function printHelp(version) {
2341
+ banner(version);
2342
+ console.log(` ${c.bold("Usage:")} deus ${c.dim("[command] [options]")}`);
2343
+ blank();
2344
+ console.log(` ${c.bold("Commands:")}`);
2345
+ console.log(` ${c.cyan("(none)")} Auto-detect: launch desktop app or start server`);
2346
+ console.log(
2347
+ ` ${c.cyan("start")} Start headless server ${c.dim("(backend + agent-server)")}`
2348
+ );
2349
+ console.log(` ${c.cyan("install")} Download and install the desktop app`);
2350
+ console.log(` ${c.cyan("pair")} Generate a pairing code for remote access`);
2351
+ console.log(` ${c.cyan("login")} Configure AI agent authentication`);
2352
+ console.log(` ${c.cyan("status")} Show server info and connected devices`);
2353
+ blank();
2354
+ console.log(` ${c.bold("Options")} ${c.dim("(start):")}`);
2355
+ console.log(` ${c.cyan("--data-dir")} Directory for database and data files`);
2356
+ blank();
2357
+ console.log(` ${c.bold("Options")} ${c.dim("(install):")}`);
2358
+ console.log(
2359
+ ` ${c.cyan("--version")} Install a specific version ${c.dim("(default: latest)")}`
2360
+ );
2361
+ blank();
2362
+ console.log(` ${c.bold("Examples:")}`);
2363
+ console.log(` ${c.dim("$")} deus ${c.dim("# auto-detect mode")}`);
2364
+ console.log(` ${c.dim("$")} deus start ${c.dim("# run headless server")}`);
2365
+ console.log(` ${c.dim("$")} deus install ${c.dim("# install desktop app")}`);
2366
+ console.log(` ${c.dim("$")} deus pair ${c.dim("# generate pairing code")}`);
2367
+ console.log(` ${c.dim("$")} deus login ${c.dim("# set up AI agent auth")}`);
2368
+ console.log(` ${c.dim("$")} deus status ${c.dim("# show server info")}`);
2369
+ blank();
2370
+ console.log(` ${c.bold("Quick start:")}`);
2371
+ console.log(
2372
+ ` ${c.dim("$")} npx deus-machine ${c.dim("# just works, no install needed")}`
2373
+ );
2374
+ blank();
2375
+ }
2376
+ async function main() {
2377
+ const args = process.argv.slice(2);
2378
+ const command = args[0] && !args[0].startsWith("-") ? args[0] : null;
2379
+ const commandArgs = command ? args.slice(1) : args;
2380
+ const pkg = await Promise.resolve().then(() => (init_package(), package_exports));
2381
+ const version = pkg.default.version;
2382
+ if (args.includes("--version") || args.includes("-v")) {
2383
+ console.log(version);
2384
+ process.exit(0);
2385
+ }
2386
+ if (command === "help" || args.includes("--help") || args.includes("-h")) {
2387
+ printHelp(version);
2388
+ process.exit(0);
2389
+ }
2390
+ const quickCommands = ["pair", "status", "login"];
2391
+ if (!command || !quickCommands.includes(command)) {
2392
+ await animatedBanner(version);
2393
+ }
2394
+ const resolvedCommand = command ?? autoDetect();
2395
+ switch (resolvedCommand) {
2396
+ // ── Start headless server ──────────────────────────────────────
2397
+ case "start":
2398
+ case "serve": {
2399
+ const { values } = parseArgs({
2400
+ args: commandArgs,
2401
+ options: {
2402
+ "data-dir": { type: "string" }
2403
+ },
2404
+ strict: false
2405
+ });
2406
+ await start({
2407
+ dataDir: values["data-dir"]
2408
+ });
2409
+ break;
2410
+ }
2411
+ // ── Install desktop app ────────────────────────────────────────
2412
+ case "install":
2413
+ case "desktop": {
2414
+ const installedPath = findInstalledApp();
2415
+ if (installedPath) {
2416
+ success(`Deus is installed at ${c.dim(installedPath)}`);
2417
+ blank();
2418
+ launchDesktop(installedPath);
2419
+ success("Launching Deus...");
2420
+ blank();
2421
+ return;
2422
+ }
2423
+ const { values } = parseArgs({
2424
+ args: commandArgs,
2425
+ options: {
2426
+ version: { type: "string", default: "latest" }
2427
+ },
2428
+ strict: false
2429
+ });
2430
+ await installDesktop({ version: values.version });
2431
+ break;
2432
+ }
2433
+ // ── Generate pairing code ──────────────────────────────────────
2434
+ case "pair": {
2435
+ await pair();
2436
+ break;
2437
+ }
2438
+ // ── Auth setup ─────────────────────────────────────────────────
2439
+ case "login": {
2440
+ blank();
2441
+ await runAuthSetup({ force: true });
2442
+ blank();
2443
+ break;
2444
+ }
2445
+ // ── Server status ──────────────────────────────────────────────
2446
+ case "status": {
2447
+ await showStatus();
2448
+ break;
2449
+ }
2450
+ default:
2451
+ error(`Unknown command: ${c.bold(resolvedCommand)}`);
2452
+ blank();
2453
+ hint(`Run ${c.cyan("deus --help")} to see available commands.`);
2454
+ blank();
2455
+ process.exit(1);
2456
+ }
2457
+ }
2458
+ function autoDetect() {
2459
+ if (!hasDisplay()) {
2460
+ info("No display detected " + c.dim("\u2014 starting in server mode"));
2461
+ blank();
2462
+ return "start";
2463
+ }
2464
+ const installedPath = findInstalledApp();
2465
+ if (installedPath) {
2466
+ return "install";
2467
+ }
2468
+ info("Desktop environment detected " + c.dim("\u2014 installing Deus app"));
2469
+ blank();
2470
+ return "install";
2471
+ }
2472
+ main().catch((err) => {
2473
+ blank();
2474
+ error(`${err.message || err}`);
2475
+ blank();
2476
+ process.exit(1);
2477
+ });