office-core 0.1.4 → 0.2.0
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/.runtime-dist/scripts/build-windows-release.js +54 -0
- package/.runtime-dist/scripts/home-agent-host.js +9 -18
- package/.runtime-dist/scripts/host-config-command.js +33 -0
- package/.runtime-dist/scripts/host-doctor.js +18 -13
- package/.runtime-dist/scripts/host-login.js +13 -11
- package/.runtime-dist/scripts/host-logs.js +22 -0
- package/.runtime-dist/scripts/host-menu.js +1 -1
- package/.runtime-dist/scripts/host-open.js +7 -10
- package/.runtime-dist/scripts/host-support-bundle.js +43 -0
- package/.runtime-dist/scripts/install-host.js +88 -111
- package/.runtime-dist/scripts/lib/cli-support.js +199 -0
- package/.runtime-dist/scripts/lib/host-config.js +91 -57
- package/.runtime-dist/scripts/lib/local-runner.js +16 -3
- package/.runtime-dist/scripts/office-cli.js +154 -223
- package/README.md +57 -94
- package/bin/double-penetration-host.mjs +1 -82
- package/bin/office-core.mjs +95 -0
- package/package.json +11 -7
- package/public/index.html +373 -1
- package/public/install-host.ps1 +7 -5
|
@@ -3,43 +3,25 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import process from "node:process";
|
|
6
|
-
import { loadHostConfig, upsertHostConfig, getHostConfigPath } from "./lib/host-config.js";
|
|
7
|
-
import {
|
|
6
|
+
import { loadHostConfig, upsertHostConfig, getHostConfigPath, getHostLogPath, getSupportBundlesDir } from "./lib/host-config.js";
|
|
7
|
+
import { assertRunnerAvailable, probeRunnerAvailability } from "./lib/local-runner.js";
|
|
8
8
|
import { sessions, hooks, buildRuntimeConfig, startDaemonLoop, stopDaemonLoop, stopSession, buildSpawnContext, spawnInteractiveSession, persistSessions, postRoomMessageUpsert, postJson, getJson, resolveProjectWorkdir, } from "./home-agent-host.js";
|
|
9
|
+
import { createSupportBundle, openExternal, readRecentLines } from "./lib/cli-support.js";
|
|
9
10
|
// ─── ANSI Codes ─────────────────────────────────────────────────────────────
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let USE_UNICODE = true;
|
|
25
|
-
function configureOutputMode(isInteractiveTerminal) {
|
|
26
|
-
const styled = isInteractiveTerminal && !("NO_COLOR" in process.env) && process.env.TERM !== "dumb";
|
|
27
|
-
USE_UNICODE = isInteractiveTerminal && process.env.OFFICE_CORE_ASCII !== "1";
|
|
28
|
-
R = styled ? "\x1b[0m" : "";
|
|
29
|
-
BOLD = styled ? "\x1b[1m" : "";
|
|
30
|
-
DIM = styled ? "\x1b[2m" : "";
|
|
31
|
-
BMAG = styled ? "\x1b[95m" : "";
|
|
32
|
-
MAG = styled ? "\x1b[35m" : "";
|
|
33
|
-
BBLU = styled ? "\x1b[94m" : "";
|
|
34
|
-
BLU = styled ? "\x1b[34m" : "";
|
|
35
|
-
BCYN = styled ? "\x1b[96m" : "";
|
|
36
|
-
CYN = styled ? "\x1b[36m" : "";
|
|
37
|
-
GRN = styled ? "\x1b[32m" : "";
|
|
38
|
-
YEL = styled ? "\x1b[33m" : "";
|
|
39
|
-
RED = styled ? "\x1b[31m" : "";
|
|
40
|
-
WHT = styled ? "\x1b[97m" : "";
|
|
41
|
-
GRY = styled ? "\x1b[90m" : "";
|
|
42
|
-
}
|
|
11
|
+
const R = "\x1b[0m";
|
|
12
|
+
const BOLD = "\x1b[1m";
|
|
13
|
+
const DIM = "\x1b[2m";
|
|
14
|
+
const BMAG = "\x1b[95m";
|
|
15
|
+
const MAG = "\x1b[35m";
|
|
16
|
+
const BBLU = "\x1b[94m";
|
|
17
|
+
const BLU = "\x1b[34m";
|
|
18
|
+
const BCYN = "\x1b[96m";
|
|
19
|
+
const CYN = "\x1b[36m";
|
|
20
|
+
const GRN = "\x1b[32m";
|
|
21
|
+
const YEL = "\x1b[33m";
|
|
22
|
+
const RED = "\x1b[31m";
|
|
23
|
+
const WHT = "\x1b[97m";
|
|
24
|
+
const GRY = "\x1b[90m";
|
|
43
25
|
// ─── ASCII Banner ───────────────────────────────────────────────────────────
|
|
44
26
|
const BANNER = [
|
|
45
27
|
[" ██████ ███████ ███████ ██ ██████ ███████ ", BMAG],
|
|
@@ -97,15 +79,9 @@ function drawAgentBox(label, text, maxW = 74) {
|
|
|
97
79
|
const lines = wordWrap(text.trim(), maxW - 4);
|
|
98
80
|
const inner = Math.max(label.length + 2, ...lines.map((l) => l.length), 20);
|
|
99
81
|
const w = Math.min(inner, maxW - 4);
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const br = USE_UNICODE ? "┘" : "+";
|
|
104
|
-
const h = USE_UNICODE ? "─" : "-";
|
|
105
|
-
const v = USE_UNICODE ? "│" : "|";
|
|
106
|
-
const top = `${CYN}${tl}${h} ${R}${BOLD}${BCYN}${label}${R}${CYN} ${h.repeat(Math.max(0, w - label.length - 1))}${tr}${R}`;
|
|
107
|
-
const bot = `${CYN}${bl}${h.repeat(w + 2)}${br}${R}`;
|
|
108
|
-
const body = lines.map((l) => `${CYN}${v}${R} ${WHT}${l.padEnd(w)}${R} ${CYN}${v}${R}`);
|
|
82
|
+
const top = `${CYN}┌─ ${R}${BOLD}${BCYN}${label}${R}${CYN} ${"─".repeat(Math.max(0, w - label.length - 1))}┐${R}`;
|
|
83
|
+
const bot = `${CYN}└${"─".repeat(w + 2)}┘${R}`;
|
|
84
|
+
const body = lines.map((l) => `${CYN}│${R} ${WHT}${l.padEnd(w)}${R} ${CYN}│${R}`);
|
|
109
85
|
return [top, ...body, bot].join("\n");
|
|
110
86
|
}
|
|
111
87
|
// ─── Timestamp ──────────────────────────────────────────────────────────────
|
|
@@ -136,6 +112,8 @@ const CMDS = [
|
|
|
136
112
|
{ name: "setup", desc: "Run first-time setup wizard", fn: cmdSetup },
|
|
137
113
|
{ name: "login", args: "<codex|claude>", desc: "Authenticate a runner", fn: cmdLogin },
|
|
138
114
|
{ name: "open", desc: "Open dashboard in browser", fn: cmdOpen },
|
|
115
|
+
{ name: "logs", args: "[tail]", desc: "Show recent host logs", fn: cmdLogs },
|
|
116
|
+
{ name: "support", alias: ["bundle"], desc: "Export a support bundle", fn: cmdSupport },
|
|
139
117
|
{ name: "project", args: "[id]", desc: "Show or switch project", fn: cmdProject },
|
|
140
118
|
{ name: "clear", alias: ["cls"], desc: "Clear screen", fn: cmdClear },
|
|
141
119
|
{ name: "help", alias: ["h", "?"], desc: "Show available commands", fn: cmdHelp },
|
|
@@ -257,26 +235,6 @@ function completer(line) {
|
|
|
257
235
|
function ask(rl, prompt) {
|
|
258
236
|
return new Promise((resolve) => rl.question(prompt, (a) => resolve(a?.trim() ?? "")));
|
|
259
237
|
}
|
|
260
|
-
async function readAllStdin(stream) {
|
|
261
|
-
let buffer = "";
|
|
262
|
-
stream.setEncoding?.("utf8");
|
|
263
|
-
for await (const chunk of stream) {
|
|
264
|
-
buffer += String(chunk);
|
|
265
|
-
}
|
|
266
|
-
return buffer;
|
|
267
|
-
}
|
|
268
|
-
function createNonInteractiveReadline() {
|
|
269
|
-
const noop = () => undefined;
|
|
270
|
-
return {
|
|
271
|
-
close: noop,
|
|
272
|
-
question: (_query, callback) => {
|
|
273
|
-
callback("");
|
|
274
|
-
return undefined;
|
|
275
|
-
},
|
|
276
|
-
prompt: noop,
|
|
277
|
-
setPrompt: noop,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
238
|
function safePrompt(ctx, preserveCursor = false) {
|
|
281
239
|
if (!ctx.running || !ctx.isInteractiveTerminal) {
|
|
282
240
|
return;
|
|
@@ -288,11 +246,23 @@ function safePrompt(ctx, preserveCursor = false) {
|
|
|
288
246
|
// Ignore prompt attempts after readline closes during shutdown/tests.
|
|
289
247
|
}
|
|
290
248
|
}
|
|
291
|
-
|
|
249
|
+
function replaceInputLine(rl, value) {
|
|
250
|
+
const internal = rl;
|
|
251
|
+
internal.line = value;
|
|
252
|
+
internal.cursor = value.length;
|
|
253
|
+
internal._refreshLine?.();
|
|
254
|
+
}
|
|
255
|
+
function startDaemonInBackground(ctx) {
|
|
292
256
|
if (!ctx.runtime) {
|
|
293
257
|
return;
|
|
294
258
|
}
|
|
295
|
-
|
|
259
|
+
void startDaemonLoop(ctx.runtime).catch((error) => {
|
|
260
|
+
if (!ctx.running) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
console.log(`${RED}Daemon failed: ${error instanceof Error ? error.message : String(error)}${R}`);
|
|
264
|
+
safePrompt(ctx);
|
|
265
|
+
});
|
|
296
266
|
}
|
|
297
267
|
function fmtAge(ms) {
|
|
298
268
|
if (ms < 60_000)
|
|
@@ -542,7 +512,7 @@ async function cmdSetup(_a, ctx) {
|
|
|
542
512
|
// Connect immediately
|
|
543
513
|
stopDaemonLoop();
|
|
544
514
|
ctx.runtime = await buildRuntimeConfig();
|
|
545
|
-
|
|
515
|
+
startDaemonInBackground(ctx);
|
|
546
516
|
console.log(`\n ${GRN}Host online.${R}\n`);
|
|
547
517
|
}
|
|
548
518
|
catch (e) {
|
|
@@ -555,7 +525,7 @@ async function cmdLogin(a, _ctx) {
|
|
|
555
525
|
console.log(`${RED}Usage: /login <codex|claude>${R}`);
|
|
556
526
|
return;
|
|
557
527
|
}
|
|
558
|
-
const cmd =
|
|
528
|
+
const cmd = assertRunnerAvailable(runner);
|
|
559
529
|
const args = runner === "codex" ? ["login"] : ["auth"];
|
|
560
530
|
console.log(`${DIM}Running ${runner} auth...${R}`);
|
|
561
531
|
await new Promise((resolve) => {
|
|
@@ -571,9 +541,37 @@ async function cmdOpen(_a, ctx) {
|
|
|
571
541
|
return;
|
|
572
542
|
}
|
|
573
543
|
const url = `${cfg.base_url}/?projectId=${encodeURIComponent(cfg.project_id)}`;
|
|
574
|
-
|
|
544
|
+
await openExternal(url);
|
|
575
545
|
console.log(`${DIM}Opened ${url}${R}`);
|
|
576
546
|
}
|
|
547
|
+
async function cmdLogs(a, _ctx) {
|
|
548
|
+
const tail = Math.max(20, Number.parseInt(a.trim() || '120', 10) || 120);
|
|
549
|
+
const lines = await readRecentLines(getHostLogPath(), tail);
|
|
550
|
+
console.log(`
|
|
551
|
+
${BOLD}Logs${R} ${DIM}${getHostLogPath()}${R}`);
|
|
552
|
+
if (!lines.length) {
|
|
553
|
+
console.log(` ${DIM}No host logs yet.${R}
|
|
554
|
+
`);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
for (const line of lines) {
|
|
558
|
+
console.log(` ${line}`);
|
|
559
|
+
}
|
|
560
|
+
console.log('');
|
|
561
|
+
}
|
|
562
|
+
async function cmdSupport(_a, _ctx) {
|
|
563
|
+
const bundle = await createSupportBundle({
|
|
564
|
+
outputRoot: getSupportBundlesDir(),
|
|
565
|
+
bundleNamePrefix: 'office-core-support',
|
|
566
|
+
includePaths: [{ source: getHostConfigPath(), target: 'config/host-config.json' }, { source: getHostLogPath(), target: 'logs/office-host.log' }],
|
|
567
|
+
metadata: { generated_at: new Date().toISOString(), cwd: process.cwd() },
|
|
568
|
+
});
|
|
569
|
+
console.log(`
|
|
570
|
+
${BOLD}Support bundle${R}`);
|
|
571
|
+
console.log(` ${bundle.outputPath}`);
|
|
572
|
+
console.log(` ${DIM}${bundle.mode}${R}
|
|
573
|
+
`);
|
|
574
|
+
}
|
|
577
575
|
async function cmdProject(a, ctx) {
|
|
578
576
|
if (!ctx.runtime) {
|
|
579
577
|
console.log(`${RED}Not connected.${R}`);
|
|
@@ -611,10 +609,9 @@ async function cmdClear(_a, _ctx) {
|
|
|
611
609
|
printBanner();
|
|
612
610
|
}
|
|
613
611
|
async function cmdHelp(_a, _ctx) {
|
|
614
|
-
const rule = (USE_UNICODE ? "─" : "-").repeat(56);
|
|
615
612
|
console.log("");
|
|
616
613
|
console.log(` ${BOLD}${WHT}Commands${R}`);
|
|
617
|
-
console.log(` ${
|
|
614
|
+
console.log(` ${"─".repeat(56)}`);
|
|
618
615
|
for (const c of CMDS) {
|
|
619
616
|
const a = c.args ? ` ${CYN}${c.args}${R}` : "";
|
|
620
617
|
const al = c.alias ? ` ${GRY}(${c.alias.map((x) => `/${x}`).join(", ")})${R}` : "";
|
|
@@ -625,81 +622,80 @@ async function cmdHelp(_a, _ctx) {
|
|
|
625
622
|
console.log(` ${DIM}Tab to autocomplete slash commands${R}\n`);
|
|
626
623
|
}
|
|
627
624
|
async function cmdQuit(_a, ctx) {
|
|
625
|
+
ctx.running = false;
|
|
626
|
+
stopDaemonLoop();
|
|
628
627
|
ctx.rl.close();
|
|
629
628
|
}
|
|
630
629
|
// ─── Main ───────────────────────────────────────────────────────────────────
|
|
631
630
|
void main().catch((err) => {
|
|
632
|
-
console.error(err
|
|
631
|
+
console.error(err);
|
|
633
632
|
process.exitCode = 1;
|
|
634
633
|
});
|
|
635
634
|
async function main() {
|
|
635
|
+
console.clear();
|
|
636
|
+
printBanner();
|
|
636
637
|
const isInteractiveTerminal = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
const rl = isInteractiveTerminal
|
|
644
|
-
? createInterface({
|
|
645
|
-
input: process.stdin,
|
|
646
|
-
output: process.stdout,
|
|
647
|
-
terminal: true,
|
|
648
|
-
historySize: 0,
|
|
649
|
-
completer,
|
|
650
|
-
})
|
|
651
|
-
: createNonInteractiveReadline();
|
|
638
|
+
const rl = createInterface({
|
|
639
|
+
input: process.stdin,
|
|
640
|
+
output: process.stdout,
|
|
641
|
+
terminal: isInteractiveTerminal,
|
|
642
|
+
historySize: 0,
|
|
643
|
+
});
|
|
652
644
|
const ctx = { runtime: null, rl, running: true, isInteractiveTerminal };
|
|
653
645
|
const sentIds = new Set();
|
|
654
|
-
const startupMessages = [];
|
|
655
|
-
let startupComplete = false;
|
|
656
|
-
let pendingInputs = 0;
|
|
657
|
-
let closeRequested = false;
|
|
658
|
-
let closeFinalized = false;
|
|
659
646
|
let finishStartup;
|
|
660
647
|
const startupReady = new Promise((resolve) => {
|
|
661
648
|
finishStartup = resolve;
|
|
662
649
|
});
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
};
|
|
672
|
-
const finalizeClose = () => {
|
|
673
|
-
if (closeFinalized) {
|
|
674
|
-
return;
|
|
650
|
+
// ── REPL ──
|
|
651
|
+
if (ctx.isInteractiveTerminal) {
|
|
652
|
+
rl.setPrompt(`${BMAG}>${R} `);
|
|
653
|
+
safePrompt(ctx);
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
try {
|
|
657
|
+
rl.resume();
|
|
675
658
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
stopDaemonLoop();
|
|
679
|
-
if (ctx.isInteractiveTerminal) {
|
|
680
|
-
process.stdin.setRawMode?.(false);
|
|
659
|
+
catch {
|
|
660
|
+
// stdin may already be closed in redirected test harnesses
|
|
681
661
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
662
|
+
}
|
|
663
|
+
// Live slash menu on keypress
|
|
664
|
+
if (ctx.isInteractiveTerminal) {
|
|
665
|
+
emitKeypressEvents(process.stdin, rl);
|
|
666
|
+
process.stdin.setRawMode?.(true);
|
|
667
|
+
process.stdin.on("keypress", (_chunk, key) => {
|
|
668
|
+
if (!ctx.running)
|
|
669
|
+
return;
|
|
670
|
+
const line = rl.line ?? "";
|
|
671
|
+
if (line.startsWith("/") && line.length >= 1) {
|
|
672
|
+
const hits = getSlashMatches(line.slice(1));
|
|
673
|
+
if (hits.length > 0 && (key?.name === "up" || key?.name === "down")) {
|
|
674
|
+
_menuSelection =
|
|
675
|
+
key.name === "up"
|
|
676
|
+
? (_menuSelection + hits.length - 1) % hits.length
|
|
677
|
+
: (_menuSelection + 1) % hits.length;
|
|
678
|
+
scheduleSlashMenuRefresh(rl, true);
|
|
679
|
+
return;
|
|
687
680
|
}
|
|
688
|
-
|
|
689
|
-
|
|
681
|
+
if (hits.length > 0 && key?.name === "tab") {
|
|
682
|
+
const selected = hits[_menuSelection] ?? hits[0];
|
|
683
|
+
replaceInputLine(rl, `/${selected.name} `);
|
|
684
|
+
scheduleSlashMenuRefresh(rl, false);
|
|
685
|
+
return;
|
|
690
686
|
}
|
|
687
|
+
scheduleSlashMenuRefresh(rl, false);
|
|
691
688
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
689
|
+
else {
|
|
690
|
+
clearSlashMenu();
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
rl.on("line", async (raw) => {
|
|
697
695
|
clearSlashMenu();
|
|
698
696
|
const input = raw.trim();
|
|
699
697
|
if (!input) {
|
|
700
|
-
|
|
701
|
-
safePrompt(ctx);
|
|
702
|
-
}
|
|
698
|
+
safePrompt(ctx);
|
|
703
699
|
return;
|
|
704
700
|
}
|
|
705
701
|
await startupReady;
|
|
@@ -728,103 +724,44 @@ async function main() {
|
|
|
728
724
|
}
|
|
729
725
|
}
|
|
730
726
|
}
|
|
731
|
-
else if (!ctx.runtime) {
|
|
732
|
-
console.log(`${RED}Not connected. Run /setup${R}`);
|
|
733
|
-
}
|
|
734
727
|
else {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
try {
|
|
739
|
-
const result = await postRoomMessageUpsert(ctx.runtime, {
|
|
740
|
-
message_id: messageId,
|
|
741
|
-
author_type: "user",
|
|
742
|
-
author_id: ctx.runtime.hostId,
|
|
743
|
-
author_label: "You",
|
|
744
|
-
text: input,
|
|
745
|
-
});
|
|
746
|
-
if (result?.message_id && result.message_id !== messageId) {
|
|
747
|
-
sentIds.add(String(result.message_id));
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
catch (e) {
|
|
751
|
-
sentIds.delete(messageId);
|
|
752
|
-
console.log(`${RED}Send failed: ${e instanceof Error ? e.message : String(e)}${R}`);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
safePrompt(ctx);
|
|
756
|
-
};
|
|
757
|
-
// ── REPL ──
|
|
758
|
-
if (ctx.isInteractiveTerminal) {
|
|
759
|
-
rl.setPrompt(`${BMAG}>${R} `);
|
|
760
|
-
}
|
|
761
|
-
// Live slash menu on keypress
|
|
762
|
-
if (ctx.isInteractiveTerminal) {
|
|
763
|
-
emitKeypressEvents(process.stdin, rl);
|
|
764
|
-
process.stdin.setRawMode?.(true);
|
|
765
|
-
process.stdin.on("keypress", (chunk, key) => {
|
|
766
|
-
if (!ctx.running || !startupComplete)
|
|
767
|
-
return;
|
|
768
|
-
const line = rl.line ?? "";
|
|
769
|
-
if (line.startsWith("/") && line.length >= 1) {
|
|
770
|
-
const hits = getSlashMatches(line.slice(1));
|
|
771
|
-
const isTab = key?.name === "tab" || chunk === "\t";
|
|
772
|
-
if (hits.length > 0 && (key?.name === "up" || key?.name === "down")) {
|
|
773
|
-
_menuSelection =
|
|
774
|
-
key.name === "up"
|
|
775
|
-
? (_menuSelection + hits.length - 1) % hits.length
|
|
776
|
-
: (_menuSelection + 1) % hits.length;
|
|
777
|
-
scheduleSlashMenuRefresh(rl, true);
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
if (!isTab) {
|
|
781
|
-
scheduleSlashMenuRefresh(rl, false);
|
|
782
|
-
}
|
|
728
|
+
// Send room message
|
|
729
|
+
if (!ctx.runtime) {
|
|
730
|
+
console.log(`${RED}Not connected. Run /setup${R}`);
|
|
783
731
|
}
|
|
784
732
|
else {
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}
|
|
789
|
-
if (ctx.isInteractiveTerminal) {
|
|
790
|
-
rl.on("line", async (raw) => {
|
|
791
|
-
pendingInputs += 1;
|
|
792
|
-
try {
|
|
793
|
-
await handleInput(raw);
|
|
794
|
-
}
|
|
795
|
-
finally {
|
|
796
|
-
pendingInputs = Math.max(0, pendingInputs - 1);
|
|
797
|
-
if (closeRequested && pendingInputs === 0) {
|
|
798
|
-
finalizeClose();
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
});
|
|
802
|
-
rl.on("close", () => {
|
|
803
|
-
closeRequested = true;
|
|
804
|
-
if (pendingInputs === 0) {
|
|
805
|
-
finalizeClose();
|
|
806
|
-
}
|
|
807
|
-
});
|
|
808
|
-
}
|
|
809
|
-
if (!ctx.isInteractiveTerminal) {
|
|
810
|
-
void (async () => {
|
|
811
|
-
const pipedInput = await pipedInputPromise;
|
|
812
|
-
const lines = pipedInput.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
813
|
-
for (const line of lines) {
|
|
814
|
-
pendingInputs += 1;
|
|
733
|
+
const messageId = `msg_${crypto.randomUUID()}`;
|
|
734
|
+
sentIds.add(messageId);
|
|
735
|
+
console.log(`${GRN}[${ts()}]${R} ${BOLD}You${R}: ${input}`);
|
|
815
736
|
try {
|
|
816
|
-
await
|
|
737
|
+
const result = await postRoomMessageUpsert(ctx.runtime, {
|
|
738
|
+
message_id: messageId,
|
|
739
|
+
author_type: "user",
|
|
740
|
+
author_id: ctx.runtime.hostId,
|
|
741
|
+
author_label: "You",
|
|
742
|
+
text: input,
|
|
743
|
+
});
|
|
744
|
+
if (result?.message_id && result.message_id !== messageId) {
|
|
745
|
+
sentIds.add(String(result.message_id));
|
|
746
|
+
}
|
|
817
747
|
}
|
|
818
|
-
|
|
819
|
-
|
|
748
|
+
catch (e) {
|
|
749
|
+
sentIds.delete(messageId);
|
|
750
|
+
console.log(`${RED}Send failed: ${e instanceof Error ? e.message : String(e)}${R}`);
|
|
820
751
|
}
|
|
821
752
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
753
|
+
}
|
|
754
|
+
safePrompt(ctx);
|
|
755
|
+
});
|
|
756
|
+
rl.on("close", () => {
|
|
757
|
+
ctx.running = false;
|
|
758
|
+
stopDaemonLoop();
|
|
759
|
+
if (ctx.isInteractiveTerminal) {
|
|
760
|
+
process.stdin.setRawMode?.(false);
|
|
761
|
+
}
|
|
762
|
+
console.log(`\n${DIM}Goodbye.${R}`);
|
|
763
|
+
process.exit(0);
|
|
764
|
+
});
|
|
828
765
|
// ── Try to connect ──
|
|
829
766
|
const cfg = await loadHostConfig();
|
|
830
767
|
if (cfg) {
|
|
@@ -842,10 +779,6 @@ async function main() {
|
|
|
842
779
|
sentIds.delete(msg.message_id);
|
|
843
780
|
return;
|
|
844
781
|
}
|
|
845
|
-
if (!startupComplete) {
|
|
846
|
-
startupMessages.push(msg);
|
|
847
|
-
return;
|
|
848
|
-
}
|
|
849
782
|
process.stdout.write(`\r\x1b[K`);
|
|
850
783
|
displayMessage(msg);
|
|
851
784
|
safePrompt(ctx, true);
|
|
@@ -853,17 +786,16 @@ async function main() {
|
|
|
853
786
|
hooks.onSessionChange = () => {
|
|
854
787
|
// Session list changed - could update status bar
|
|
855
788
|
};
|
|
856
|
-
|
|
789
|
+
startDaemonInBackground(ctx);
|
|
857
790
|
console.log(` ${GRN}Host online.${R} Type ${GRN}/help${R} for commands.\n`);
|
|
858
|
-
|
|
791
|
+
safePrompt(ctx);
|
|
859
792
|
}
|
|
860
793
|
catch (e) {
|
|
861
794
|
console.log(` ${RED}Connection failed: ${e instanceof Error ? e.message : String(e)}${R}`);
|
|
862
795
|
console.log(` ${DIM}Run /setup to configure or /doctor to diagnose.${R}\n`);
|
|
796
|
+
safePrompt(ctx);
|
|
863
797
|
}
|
|
864
798
|
finally {
|
|
865
|
-
startupComplete = true;
|
|
866
|
-
safePrompt(ctx);
|
|
867
799
|
finishStartup();
|
|
868
800
|
}
|
|
869
801
|
}
|
|
@@ -871,7 +803,6 @@ async function main() {
|
|
|
871
803
|
printHeader(os.hostname());
|
|
872
804
|
console.log(` ${YEL}No host config found.${R}`);
|
|
873
805
|
console.log(` ${DIM}Run /setup to get started or /doctor to check prerequisites.${R}\n`);
|
|
874
|
-
startupComplete = true;
|
|
875
806
|
safePrompt(ctx);
|
|
876
807
|
finishStartup();
|
|
877
808
|
}
|