tesseract-agent 0.9.5 → 0.9.6
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-bundle/cli.js +1795 -400
- package/package.json +1 -1
package/dist-bundle/cli.js
CHANGED
|
@@ -137,7 +137,7 @@ function sealBasePaths() {
|
|
|
137
137
|
claudeConfigDir();
|
|
138
138
|
return snapshotBasePaths();
|
|
139
139
|
}
|
|
140
|
-
var STARTUP_CWD, USER_HOME, TEMP_DIR, systemDirs, envRoot, peeking;
|
|
140
|
+
var STARTUP_CWD, USER_HOME, TEMP_DIR, systemDirs, envRoot, peeking, dropsDir;
|
|
141
141
|
var init_base_paths = __esm({
|
|
142
142
|
"../../packages/shared/dist/base-paths.js"() {
|
|
143
143
|
"use strict";
|
|
@@ -147,6 +147,7 @@ var init_base_paths = __esm({
|
|
|
147
147
|
systemDirs = deriveSystemDirs(USER_HOME);
|
|
148
148
|
envRoot = memoEnvRoot();
|
|
149
149
|
peeking = false;
|
|
150
|
+
dropsDir = () => join(systemDirs.downloads, "Tesseract");
|
|
150
151
|
}
|
|
151
152
|
});
|
|
152
153
|
|
|
@@ -2399,7 +2400,7 @@ var require_websocket = __commonJS({
|
|
|
2399
2400
|
var net = __require("net");
|
|
2400
2401
|
var tls2 = __require("tls");
|
|
2401
2402
|
var { randomBytes: randomBytes9, createHash: createHash9 } = __require("crypto");
|
|
2402
|
-
var { Duplex, Readable:
|
|
2403
|
+
var { Duplex, Readable: Readable3 } = __require("stream");
|
|
2403
2404
|
var { URL: URL2 } = __require("url");
|
|
2404
2405
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
2405
2406
|
var Receiver2 = require_receiver();
|
|
@@ -22264,10 +22265,10 @@ ${r?.url ?? ""}`);
|
|
|
22264
22265
|
tool3("browser_screenshot", `Capture a PNG screenshot of the visible page (for visual inspection; use browser_snapshot to get actionable elements). ${REAL}`, {}, async () => {
|
|
22265
22266
|
const r = await drive("screenshot");
|
|
22266
22267
|
const dataUrl = r?.dataUrl ?? "";
|
|
22267
|
-
const
|
|
22268
|
-
if (!
|
|
22268
|
+
const b642 = dataUrl.startsWith("data:") ? dataUrl.slice(dataUrl.indexOf(",") + 1) : dataUrl;
|
|
22269
|
+
if (!b642)
|
|
22269
22270
|
return text2("No screenshot data returned.", true);
|
|
22270
|
-
return { content: [{ type: "image", data:
|
|
22271
|
+
return { content: [{ type: "image", data: b642, mimeType: "image/png" }] };
|
|
22271
22272
|
}),
|
|
22272
22273
|
tool3("browser_click", `Click an element by CSS selector (from browser_snapshot). ${REAL}`, { selector: external_exports.string().describe("CSS selector of the element to click.") }, async (a) => {
|
|
22273
22274
|
const r = await drive("click", { selector: a.selector });
|
|
@@ -23214,11 +23215,11 @@ function makeApi(deps, toolId, tier) {
|
|
|
23214
23215
|
};
|
|
23215
23216
|
return api;
|
|
23216
23217
|
}
|
|
23217
|
-
function buildToolCtx(deps, sessionId, toolId,
|
|
23218
|
+
function buildToolCtx(deps, sessionId, toolId, log9 = NOOP_LOG, tier = "third-party") {
|
|
23218
23219
|
const getSid = typeof sessionId === "function" ? sessionId : () => sessionId;
|
|
23219
23220
|
return { ui: makeApi(deps, toolId, tier), get sessionId() {
|
|
23220
23221
|
return getSid();
|
|
23221
|
-
}, toolId, log:
|
|
23222
|
+
}, toolId, log: log9 };
|
|
23222
23223
|
}
|
|
23223
23224
|
function wrapCtxTool(tool18, ctx2) {
|
|
23224
23225
|
const raw = tool18.__ctxHandler;
|
|
@@ -26463,9 +26464,9 @@ function sourcesEqual(a, b) {
|
|
|
26463
26464
|
if (a.length !== b.length)
|
|
26464
26465
|
return false;
|
|
26465
26466
|
const as = [...a].sort();
|
|
26466
|
-
const
|
|
26467
|
+
const bs4 = [...b].sort();
|
|
26467
26468
|
for (let i = 0; i < as.length; i++)
|
|
26468
|
-
if (as[i] !==
|
|
26469
|
+
if (as[i] !== bs4[i])
|
|
26469
26470
|
return false;
|
|
26470
26471
|
return true;
|
|
26471
26472
|
}
|
|
@@ -36728,9 +36729,9 @@ var require_encoder = __commonJS({
|
|
|
36728
36729
|
RGB_YUV_TABLE[i + 1792 >> 0] = -5329 * i;
|
|
36729
36730
|
}
|
|
36730
36731
|
}
|
|
36731
|
-
function writeBits(
|
|
36732
|
-
var value =
|
|
36733
|
-
var posval =
|
|
36732
|
+
function writeBits(bs4) {
|
|
36733
|
+
var value = bs4[0];
|
|
36734
|
+
var posval = bs4[1] - 1;
|
|
36734
36735
|
while (posval >= 0) {
|
|
36735
36736
|
if (value & 1 << posval) {
|
|
36736
36737
|
bytenew |= 1 << bytepos;
|
|
@@ -58598,12 +58599,12 @@ import { createHash as createHash8 } from "node:crypto";
|
|
|
58598
58599
|
import { execFileSync as execFileSync5, spawn as spawn12 } from "node:child_process";
|
|
58599
58600
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
58600
58601
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
58601
|
-
import { dirname as
|
|
58602
|
+
import { dirname as dirname27, join as join60, resolve as resolve11 } from "node:path";
|
|
58602
58603
|
import { createInterface } from "node:readline/promises";
|
|
58603
58604
|
|
|
58604
58605
|
// ../../packages/host-core/dist/control-link.js
|
|
58605
58606
|
import { hostname as hostname3 } from "node:os";
|
|
58606
|
-
import { join as
|
|
58607
|
+
import { join as join42, basename as basename9 } from "node:path";
|
|
58607
58608
|
import { mkdirSync as mkdirSync18 } from "node:fs";
|
|
58608
58609
|
|
|
58609
58610
|
// ../../node_modules/ws/wrapper.mjs
|
|
@@ -59030,13 +59031,27 @@ var E2ESessions = class {
|
|
|
59030
59031
|
|
|
59031
59032
|
// ../../packages/e2e/dist/seal.js
|
|
59032
59033
|
var SEAL_INFO = utf8("tesseract-seal-v1");
|
|
59034
|
+
async function openFromPrivateKey(recipientPrivate, sealed, aad = new Uint8Array()) {
|
|
59035
|
+
const shared = await ecdh(recipientPrivate, unb64u(sealed.epk));
|
|
59036
|
+
const key2 = await importAesKey(await hkdf(shared, new Uint8Array(), SEAL_INFO, 32));
|
|
59037
|
+
return aesOpen(key2, unb64u(sealed.nonce), unb64u(sealed.ct), aad);
|
|
59038
|
+
}
|
|
59033
59039
|
|
|
59034
59040
|
// ../../packages/e2e/dist/attest.js
|
|
59035
59041
|
var SEALING_ATTEST_CTX = utf8("tesseract-sealing-attest-v1");
|
|
59042
|
+
function attestMessage(sealingPubRaw) {
|
|
59043
|
+
const out = new Uint8Array(SEALING_ATTEST_CTX.length + sealingPubRaw.length);
|
|
59044
|
+
out.set(SEALING_ATTEST_CTX, 0);
|
|
59045
|
+
out.set(sealingPubRaw, SEALING_ATTEST_CTX.length);
|
|
59046
|
+
return out;
|
|
59047
|
+
}
|
|
59048
|
+
async function attestSealingKey(identityPrivate, sealingPubRaw) {
|
|
59049
|
+
return b64u(await sign(identityPrivate, attestMessage(sealingPubRaw)));
|
|
59050
|
+
}
|
|
59036
59051
|
|
|
59037
59052
|
// ../../packages/host-core/dist/daemon-agent.js
|
|
59038
59053
|
init_base_paths();
|
|
59039
|
-
import { join as
|
|
59054
|
+
import { join as join30 } from "node:path";
|
|
59040
59055
|
|
|
59041
59056
|
// ../../packages/host-core/dist/surface-hub.js
|
|
59042
59057
|
init_dist();
|
|
@@ -60712,7 +60727,7 @@ function convertTool(t) {
|
|
|
60712
60727
|
if (!t || typeof t.name !== "string" || typeof t.execute !== "function")
|
|
60713
60728
|
return null;
|
|
60714
60729
|
const shape = t.parameters?.shape ?? t.inputSchema?.shape ?? {};
|
|
60715
|
-
const
|
|
60730
|
+
const exec2 = t.execute;
|
|
60716
60731
|
return {
|
|
60717
60732
|
name: t.name,
|
|
60718
60733
|
description: t.description ?? t.name,
|
|
@@ -60720,12 +60735,12 @@ function convertTool(t) {
|
|
|
60720
60735
|
// Fallback handler — used when no surface host is wired (off-daemon / no session): the
|
|
60721
60736
|
// OpenClaw tool still runs with a DEGRADED ctx whose ui methods no-op (open → an
|
|
60722
60737
|
// already-closed handle), so `ctx.ui.notify(...)` degrades instead of throwing on `{}`.
|
|
60723
|
-
handler: async (args) => toCallToolResult(await
|
|
60738
|
+
handler: async (args) => toCallToolResult(await exec2(args, NOOP_UI_CTX)),
|
|
60724
60739
|
// Ctx-aware path (Pillar 1 P4): agent-manager's wrapCtxTool injects a per-session ctx
|
|
60725
60740
|
// ({ ui, sessionId, toolId }) here, so the OpenClaw tool's execute(args, ctx) can open
|
|
60726
60741
|
// and drive surfaces via ctx.ui. Unknown ctx members (channel/secret/log) read as
|
|
60727
60742
|
// undefined — graceful, matching the Proxy no-op contract.
|
|
60728
|
-
__ctxHandler: async (args, ctx2) => toCallToolResult(await
|
|
60743
|
+
__ctxHandler: async (args, ctx2) => toCallToolResult(await exec2(args, ctx2))
|
|
60729
60744
|
// Our narrow CallToolResult is a structural subset of the SDK's (which carries an
|
|
60730
60745
|
// index signature + a richer content union); cast through unknown intentionally.
|
|
60731
60746
|
};
|
|
@@ -62192,12 +62207,17 @@ async function closeEnginePage(browserId) {
|
|
|
62192
62207
|
}
|
|
62193
62208
|
}
|
|
62194
62209
|
|
|
62210
|
+
// ../../packages/host-core/dist/gui/windows.js
|
|
62211
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
62212
|
+
import { join as join20 } from "node:path";
|
|
62213
|
+
|
|
62195
62214
|
// ../../packages/host-core/dist/gui/daemon-gui.js
|
|
62196
62215
|
import { join as join19, dirname as dirname8 } from "node:path";
|
|
62197
62216
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
62198
62217
|
import { existsSync as existsSync9 } from "node:fs";
|
|
62199
62218
|
var el = null;
|
|
62200
62219
|
var guiReady = false;
|
|
62220
|
+
var log2 = (...a) => console.log("[daemon-gui]", ...a);
|
|
62201
62221
|
var tray = null;
|
|
62202
62222
|
function formatAccelerator(accel) {
|
|
62203
62223
|
const sym = {
|
|
@@ -62256,6 +62276,68 @@ function buildTrayMenu(deps) {
|
|
|
62256
62276
|
]);
|
|
62257
62277
|
}
|
|
62258
62278
|
var rdeskPowerBlockerId = null;
|
|
62279
|
+
var trayActivity = null;
|
|
62280
|
+
function trayTooltip() {
|
|
62281
|
+
if (process.env.CCB_RDESK_ROLE === "server")
|
|
62282
|
+
return "Tesseract Remote Desktop \u2014 this machine";
|
|
62283
|
+
if (process.env.CCB_RDESK_ROLE === "client")
|
|
62284
|
+
return "Tesseract Remote Desktop";
|
|
62285
|
+
const bits = [];
|
|
62286
|
+
if (rdeskActive > 0)
|
|
62287
|
+
bits.push(`sharing screen (${rdeskActive})`);
|
|
62288
|
+
if (trayActivity)
|
|
62289
|
+
bits.push(trayActivity);
|
|
62290
|
+
return bits.length ? `Tesseract \u2014 ${bits.join(" \xB7 ")}` : "Tesseract";
|
|
62291
|
+
}
|
|
62292
|
+
function setTrayActivity(label) {
|
|
62293
|
+
trayActivity = label;
|
|
62294
|
+
if (!tray)
|
|
62295
|
+
return;
|
|
62296
|
+
try {
|
|
62297
|
+
tray.setToolTip(trayTooltip());
|
|
62298
|
+
} catch {
|
|
62299
|
+
}
|
|
62300
|
+
}
|
|
62301
|
+
function daemonNotify(opts) {
|
|
62302
|
+
if (!guiReady || !el)
|
|
62303
|
+
return false;
|
|
62304
|
+
try {
|
|
62305
|
+
if (!el.Notification.isSupported())
|
|
62306
|
+
return false;
|
|
62307
|
+
const actions = (opts.actions ?? []).slice(0, 3);
|
|
62308
|
+
const n = new el.Notification({
|
|
62309
|
+
title: opts.title,
|
|
62310
|
+
body: opts.body,
|
|
62311
|
+
silent: opts.silent,
|
|
62312
|
+
...actions.length ? { actions: actions.map((a) => ({ type: "button", text: a.label })) } : {}
|
|
62313
|
+
});
|
|
62314
|
+
if (actions.length && opts.onAction) {
|
|
62315
|
+
n.on("action", (...a) => {
|
|
62316
|
+
const ix = typeof a[1] === "number" ? a[1] : -1;
|
|
62317
|
+
const chosen = actions[ix];
|
|
62318
|
+
if (chosen) {
|
|
62319
|
+
try {
|
|
62320
|
+
opts.onAction(chosen.id);
|
|
62321
|
+
} catch (e) {
|
|
62322
|
+
log2("notify action failed:", e instanceof Error ? e.message : String(e));
|
|
62323
|
+
}
|
|
62324
|
+
}
|
|
62325
|
+
});
|
|
62326
|
+
}
|
|
62327
|
+
if (opts.onClick)
|
|
62328
|
+
n.on("click", () => {
|
|
62329
|
+
try {
|
|
62330
|
+
opts.onClick();
|
|
62331
|
+
} catch {
|
|
62332
|
+
}
|
|
62333
|
+
});
|
|
62334
|
+
n.show();
|
|
62335
|
+
return true;
|
|
62336
|
+
} catch (e) {
|
|
62337
|
+
log2("notify failed:", e instanceof Error ? e.message : String(e));
|
|
62338
|
+
return false;
|
|
62339
|
+
}
|
|
62340
|
+
}
|
|
62259
62341
|
function setRdeskTraySessions(count, onStopAll) {
|
|
62260
62342
|
rdeskActive = count;
|
|
62261
62343
|
rdeskStopAll = onStopAll;
|
|
@@ -62277,7 +62359,7 @@ function setRdeskTraySessions(count, onStopAll) {
|
|
|
62277
62359
|
if (!tray || !trayDeps)
|
|
62278
62360
|
return;
|
|
62279
62361
|
try {
|
|
62280
|
-
tray.setToolTip(
|
|
62362
|
+
tray.setToolTip(trayTooltip());
|
|
62281
62363
|
tray.setContextMenu(buildTrayMenu(trayDeps));
|
|
62282
62364
|
} catch {
|
|
62283
62365
|
}
|
|
@@ -62352,7 +62434,7 @@ function daemonPreloadPath() {
|
|
|
62352
62434
|
// ../../packages/host-core/dist/os-control-runner.js
|
|
62353
62435
|
import { spawn as spawn6 } from "node:child_process";
|
|
62354
62436
|
var IS_MAC = process.platform === "darwin";
|
|
62355
|
-
var
|
|
62437
|
+
var log3 = (...a) => console.log("[os-control]", ...a);
|
|
62356
62438
|
function capture(command, args, opts = {}) {
|
|
62357
62439
|
return new Promise((resolve12) => {
|
|
62358
62440
|
const child = spawn6(command, args, { cwd: opts.cwd, windowsHide: true });
|
|
@@ -62499,7 +62581,7 @@ function createDaemonOsControlRunner() {
|
|
|
62499
62581
|
if (o.at)
|
|
62500
62582
|
n.moveMouse(Math.round(o.at.x), Math.round(o.at.y));
|
|
62501
62583
|
n.mouseClick(NUT_BTN[o.button ?? "left"], o.double === true);
|
|
62502
|
-
|
|
62584
|
+
log3(`${o.button ?? "left"}${o.double ? "-double" : ""}-click${o.at ? ` @ ${Math.round(o.at.x)},${Math.round(o.at.y)}` : ""}`);
|
|
62503
62585
|
},
|
|
62504
62586
|
async mouseDrag(from, to, o) {
|
|
62505
62587
|
const n = await nutOrThrow();
|
|
@@ -62561,7 +62643,7 @@ function createDaemonOsControlRunner() {
|
|
|
62561
62643
|
async typeText(t) {
|
|
62562
62644
|
const n = await nutOrThrow();
|
|
62563
62645
|
n.typeString(t);
|
|
62564
|
-
|
|
62646
|
+
log3(`type "${t.length > 50 ? `${t.slice(0, 50)}\u2026` : t}"`);
|
|
62565
62647
|
},
|
|
62566
62648
|
async pressKeys(combo) {
|
|
62567
62649
|
const n = await nutOrThrow();
|
|
@@ -62569,7 +62651,7 @@ function createDaemonOsControlRunner() {
|
|
|
62569
62651
|
if (!key2)
|
|
62570
62652
|
throw new Error(`Empty key combo: "${combo}"`);
|
|
62571
62653
|
n.keyTap(key2, modifiers);
|
|
62572
|
-
|
|
62654
|
+
log3(`key ${combo}`);
|
|
62573
62655
|
},
|
|
62574
62656
|
// App launch (the one non-input capability the voice path needs — shells `open`)
|
|
62575
62657
|
async appLaunch(app) {
|
|
@@ -62581,6 +62663,83 @@ function createDaemonOsControlRunner() {
|
|
|
62581
62663
|
}
|
|
62582
62664
|
|
|
62583
62665
|
// ../../packages/host-core/dist/gui/windows.js
|
|
62666
|
+
function uiFile(name) {
|
|
62667
|
+
return join20(daemonUiDir(), name);
|
|
62668
|
+
}
|
|
62669
|
+
function notReady(what) {
|
|
62670
|
+
throw new Error(`${what} requires the on-screen co-worker (the GUI daemon) \u2014 it isn't active. Enable it (CCB_FORCE_DAEMON_GUI=1) on a signed build.`);
|
|
62671
|
+
}
|
|
62672
|
+
var ipcWired = false;
|
|
62673
|
+
var pendingInput = /* @__PURE__ */ new Map();
|
|
62674
|
+
function wireIpcOnce() {
|
|
62675
|
+
const el2 = electronModule();
|
|
62676
|
+
if (!el2 || ipcWired)
|
|
62677
|
+
return;
|
|
62678
|
+
ipcWired = true;
|
|
62679
|
+
el2.ipcMain.on("input:submit", (_e, payload) => {
|
|
62680
|
+
const p = payload;
|
|
62681
|
+
const resolve12 = pendingInput.get(p.reqId);
|
|
62682
|
+
if (resolve12) {
|
|
62683
|
+
pendingInput.delete(p.reqId);
|
|
62684
|
+
resolve12({ submitted: !!p.submitted, values: p.values ?? {} });
|
|
62685
|
+
}
|
|
62686
|
+
});
|
|
62687
|
+
}
|
|
62688
|
+
function showFormWindow(kind, payload, signal) {
|
|
62689
|
+
const el2 = electronModule();
|
|
62690
|
+
if (!el2)
|
|
62691
|
+
notReady("Asking for input");
|
|
62692
|
+
wireIpcOnce();
|
|
62693
|
+
const reqId = randomUUID8();
|
|
62694
|
+
const win3 = new el2.BrowserWindow({
|
|
62695
|
+
width: 460,
|
|
62696
|
+
height: 320,
|
|
62697
|
+
transparent: true,
|
|
62698
|
+
frame: false,
|
|
62699
|
+
resizable: false,
|
|
62700
|
+
alwaysOnTop: true,
|
|
62701
|
+
skipTaskbar: true,
|
|
62702
|
+
fullscreenable: false,
|
|
62703
|
+
show: false,
|
|
62704
|
+
webPreferences: { preload: daemonPreloadPath(), contextIsolation: true, sandbox: false }
|
|
62705
|
+
});
|
|
62706
|
+
win3.setAlwaysOnTop(true, "screen-saver");
|
|
62707
|
+
win3.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
|
62708
|
+
void win3.loadFile(uiFile("input.html"));
|
|
62709
|
+
win3.webContents.on("did-finish-load", () => {
|
|
62710
|
+
win3.webContents.send(`${kind}:show`, { reqId, ...payload });
|
|
62711
|
+
win3.center();
|
|
62712
|
+
win3.show();
|
|
62713
|
+
win3.focus();
|
|
62714
|
+
});
|
|
62715
|
+
return new Promise((resolve12) => {
|
|
62716
|
+
let done = false;
|
|
62717
|
+
const onAbort = () => finish({ submitted: false, values: {} });
|
|
62718
|
+
const finish = (r) => {
|
|
62719
|
+
if (done)
|
|
62720
|
+
return;
|
|
62721
|
+
done = true;
|
|
62722
|
+
signal?.removeEventListener("abort", onAbort);
|
|
62723
|
+
pendingInput.delete(reqId);
|
|
62724
|
+
try {
|
|
62725
|
+
if (!win3.isDestroyed())
|
|
62726
|
+
win3.close();
|
|
62727
|
+
} catch {
|
|
62728
|
+
}
|
|
62729
|
+
resolve12(r);
|
|
62730
|
+
};
|
|
62731
|
+
pendingInput.set(reqId, finish);
|
|
62732
|
+
win3.on("closed", () => finish({ submitted: false, values: {} }));
|
|
62733
|
+
if (signal?.aborted)
|
|
62734
|
+
onAbort();
|
|
62735
|
+
else
|
|
62736
|
+
signal?.addEventListener("abort", onAbort);
|
|
62737
|
+
});
|
|
62738
|
+
}
|
|
62739
|
+
async function confirmAction(spec, opts) {
|
|
62740
|
+
const r = await showFormWindow("confirm", { spec }, opts?.signal);
|
|
62741
|
+
return { confirmed: r.submitted };
|
|
62742
|
+
}
|
|
62584
62743
|
var spotlightWin = null;
|
|
62585
62744
|
function widgetDisplayRect() {
|
|
62586
62745
|
return widgetDisplayBounds(spotlightWin) ?? null;
|
|
@@ -62598,15 +62757,15 @@ function widgetDisplayBounds(w) {
|
|
|
62598
62757
|
}
|
|
62599
62758
|
|
|
62600
62759
|
// ../../packages/host-core/dist/gui/ai-control.js
|
|
62601
|
-
import { join as
|
|
62602
|
-
import { randomUUID as
|
|
62760
|
+
import { join as join21, dirname as dirname9 } from "node:path";
|
|
62761
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
62603
62762
|
init_dist4();
|
|
62604
62763
|
var OWN_PID = process.pid;
|
|
62605
62764
|
var runner = createDaemonOsControlRunner();
|
|
62606
62765
|
var controlAbort = new AbortController();
|
|
62607
62766
|
var IDLE_RELEASE_MS = 15 * 6e4;
|
|
62608
62767
|
function uiPath(name) {
|
|
62609
|
-
return
|
|
62768
|
+
return join21(daemonUiDir(), name);
|
|
62610
62769
|
}
|
|
62611
62770
|
var consentWin = null;
|
|
62612
62771
|
var pendingConsent = /* @__PURE__ */ new Map();
|
|
@@ -62616,7 +62775,7 @@ function askRdeskConsent(opts) {
|
|
|
62616
62775
|
return Promise.resolve(false);
|
|
62617
62776
|
const disp = widgetDisplayRect() ?? el2.screen.getPrimaryDisplay().bounds;
|
|
62618
62777
|
const W = 470, H = 320;
|
|
62619
|
-
const
|
|
62778
|
+
const win3 = new el2.BrowserWindow({
|
|
62620
62779
|
x: disp.x + Math.round((disp.width - W) / 2),
|
|
62621
62780
|
y: disp.y + Math.round((disp.height - H) / 3),
|
|
62622
62781
|
width: W,
|
|
@@ -62634,15 +62793,15 @@ function askRdeskConsent(opts) {
|
|
|
62634
62793
|
backgroundColor: "#00000000",
|
|
62635
62794
|
webPreferences: { preload: daemonPreloadPath(), contextIsolation: true, sandbox: false }
|
|
62636
62795
|
});
|
|
62637
|
-
|
|
62638
|
-
|
|
62639
|
-
const reqId =
|
|
62796
|
+
win3.setAlwaysOnTop(true, "screen-saver");
|
|
62797
|
+
win3.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
|
62798
|
+
const reqId = randomUUID9();
|
|
62640
62799
|
const who = opts.viewerName ? `\u201C${opts.viewerName}\u201D` : "Someone";
|
|
62641
|
-
void
|
|
62800
|
+
void win3.loadFile(uiPath("consent.html")).then(() => {
|
|
62642
62801
|
try {
|
|
62643
|
-
|
|
62644
|
-
|
|
62645
|
-
|
|
62802
|
+
win3.webContents.send("consent:show", { reqId, title: "Allow remote desktop access?", body: `${who} wants to view and control this computer.` });
|
|
62803
|
+
win3.show();
|
|
62804
|
+
win3.focus();
|
|
62646
62805
|
el2.app.focus?.({ steal: true });
|
|
62647
62806
|
log("rdesk: consent dialog shown");
|
|
62648
62807
|
} catch (e) {
|
|
@@ -62657,16 +62816,16 @@ function askRdeskConsent(opts) {
|
|
|
62657
62816
|
done = true;
|
|
62658
62817
|
pendingConsent.delete(reqId);
|
|
62659
62818
|
try {
|
|
62660
|
-
if (!
|
|
62661
|
-
|
|
62819
|
+
if (!win3.isDestroyed())
|
|
62820
|
+
win3.close();
|
|
62662
62821
|
} catch {
|
|
62663
62822
|
}
|
|
62664
|
-
if (consentWin ===
|
|
62823
|
+
if (consentWin === win3)
|
|
62665
62824
|
consentWin = null;
|
|
62666
62825
|
resolve12(r.ok);
|
|
62667
62826
|
};
|
|
62668
62827
|
pendingConsent.set(reqId, finish);
|
|
62669
|
-
|
|
62828
|
+
win3.on("closed", () => finish({ ok: false, remember: false }));
|
|
62670
62829
|
setTimeout(() => finish({ ok: false, remember: false }), 6e4);
|
|
62671
62830
|
});
|
|
62672
62831
|
}
|
|
@@ -62676,7 +62835,7 @@ var MOONDREAM_URL = (process.env.CCB_MOONDREAM_URL?.trim() || "http://127.0.0.1:
|
|
|
62676
62835
|
// ../../packages/host-core/dist/voice-control.js
|
|
62677
62836
|
init_base_paths();
|
|
62678
62837
|
import { existsSync as existsSync10, readFileSync as readFileSync12 } from "node:fs";
|
|
62679
|
-
import { basename as basename2, join as
|
|
62838
|
+
import { basename as basename2, join as join22 } from "node:path";
|
|
62680
62839
|
|
|
62681
62840
|
// ../../packages/voice-core/dist/providers/tool-adapters.js
|
|
62682
62841
|
function toELClientTool(t) {
|
|
@@ -62936,7 +63095,7 @@ async function createElevenLabsAgent(args) {
|
|
|
62936
63095
|
}
|
|
62937
63096
|
|
|
62938
63097
|
// ../../packages/voice-core/dist/orchestrator.js
|
|
62939
|
-
import { randomUUID as
|
|
63098
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
62940
63099
|
|
|
62941
63100
|
// ../../packages/voice-core/dist/summarizer.js
|
|
62942
63101
|
var TURN_SYSTEM = [
|
|
@@ -63294,7 +63453,7 @@ Additional context: ${context}` : task;
|
|
|
63294
63453
|
this.emitFocusedState();
|
|
63295
63454
|
const sessionId = track.sessionId || void 0;
|
|
63296
63455
|
this.deps.emit({ type: "voice:dispatched", text: message, sessionId });
|
|
63297
|
-
const clientRequestId =
|
|
63456
|
+
const clientRequestId = randomUUID10();
|
|
63298
63457
|
track.pendingClientRequestId = clientRequestId;
|
|
63299
63458
|
try {
|
|
63300
63459
|
const sid = await this.deps.agent.dispatch({ message, clientRequestId, sessionId });
|
|
@@ -63327,7 +63486,7 @@ Additional context: ${context}` : task;
|
|
|
63327
63486
|
this.emitFocusedState();
|
|
63328
63487
|
const sessionId = track.sessionId || void 0;
|
|
63329
63488
|
this.deps.emit({ type: "voice:dispatched", text: message, sessionId });
|
|
63330
|
-
const clientRequestId =
|
|
63489
|
+
const clientRequestId = randomUUID10();
|
|
63331
63490
|
track.pendingClientRequestId = clientRequestId;
|
|
63332
63491
|
try {
|
|
63333
63492
|
const sid = await this.deps.agent.dispatch({ message, clientRequestId, sessionId });
|
|
@@ -63350,7 +63509,7 @@ Additional context: ${context}` : task;
|
|
|
63350
63509
|
this.voice?.respondToTool(inv.callId, "Got it \u2014 passing that along.");
|
|
63351
63510
|
this.focusTrack(track);
|
|
63352
63511
|
this.deps.emit({ type: "voice:dispatched", text: addendum, sessionId: track.sessionId });
|
|
63353
|
-
const clientRequestId =
|
|
63512
|
+
const clientRequestId = randomUUID10();
|
|
63354
63513
|
track.pendingClientRequestId = clientRequestId;
|
|
63355
63514
|
try {
|
|
63356
63515
|
await this.deps.agent.dispatch({ message: addendum, sessionId: track.sessionId, clientRequestId });
|
|
@@ -63388,7 +63547,7 @@ Additional context: ${context}` : task;
|
|
|
63388
63547
|
this.voice?.respondToTool(inv.callId, "Got it \u2014 passing that along.");
|
|
63389
63548
|
this.focusTrack(track);
|
|
63390
63549
|
this.deps.emit({ type: "voice:dispatched", text: answer, sessionId: track.sessionId });
|
|
63391
|
-
const clientRequestId =
|
|
63550
|
+
const clientRequestId = randomUUID10();
|
|
63392
63551
|
track.pendingClientRequestId = clientRequestId;
|
|
63393
63552
|
void this.deps.agent.dispatch({ message: answer, sessionId: track.sessionId, clientRequestId }).catch((err2) => this.say(`I couldn't send that: ${errMsg(err2)}.`, { urgent: true }));
|
|
63394
63553
|
track.state = "working";
|
|
@@ -63921,7 +64080,7 @@ async function mintOpenAi(apiKey, model, voice) {
|
|
|
63921
64080
|
function readGeminiKeyFile() {
|
|
63922
64081
|
try {
|
|
63923
64082
|
const home = tesseractHome();
|
|
63924
|
-
const f =
|
|
64083
|
+
const f = join22(home, "daemon", "gemini.key");
|
|
63925
64084
|
if (existsSync10(f)) {
|
|
63926
64085
|
const k = readFileSync12(f, "utf-8").trim();
|
|
63927
64086
|
if (k)
|
|
@@ -64345,9 +64504,9 @@ import { mkdirSync as mkdirSync14 } from "node:fs";
|
|
|
64345
64504
|
|
|
64346
64505
|
// ../../packages/host-core/dist/image-gen-runner.js
|
|
64347
64506
|
init_base_paths();
|
|
64348
|
-
import { randomUUID as
|
|
64507
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
64349
64508
|
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, readFileSync as readFileSync13 } from "node:fs";
|
|
64350
|
-
import { join as
|
|
64509
|
+
import { join as join23 } from "node:path";
|
|
64351
64510
|
|
|
64352
64511
|
// ../../packages/image-edit-core/dist/transparent.js
|
|
64353
64512
|
var import_fast_png = __toESM(require_lib(), 1);
|
|
@@ -64552,8 +64711,8 @@ var ImageEditError = class extends Error {
|
|
|
64552
64711
|
function toBase64(bytes2) {
|
|
64553
64712
|
return Buffer.from(bytes2).toString("base64");
|
|
64554
64713
|
}
|
|
64555
|
-
function fromBase64(
|
|
64556
|
-
return new Uint8Array(Buffer.from(
|
|
64714
|
+
function fromBase64(b642) {
|
|
64715
|
+
return new Uint8Array(Buffer.from(b642, "base64"));
|
|
64557
64716
|
}
|
|
64558
64717
|
function toBlob(img) {
|
|
64559
64718
|
return new Blob([img.bytes], { type: img.mime });
|
|
@@ -64694,7 +64853,7 @@ var OpenAiImageProvider = class {
|
|
|
64694
64853
|
if (!res.ok) {
|
|
64695
64854
|
throw new ImageEditError(json2.error?.message ?? `OpenAI request failed (${res.status})`, this.id, res.status);
|
|
64696
64855
|
}
|
|
64697
|
-
const out = (json2.data ?? []).map((d) => d.b64_json).filter((b) => !!b).map((
|
|
64856
|
+
const out = (json2.data ?? []).map((d) => d.b64_json).filter((b) => !!b).map((b642) => ({ bytes: fromBase64(b642), mime: "image/png" }));
|
|
64698
64857
|
if (!out.length)
|
|
64699
64858
|
throw new ImageEditError("OpenAI returned no image", this.id, res.status);
|
|
64700
64859
|
return out;
|
|
@@ -64723,10 +64882,10 @@ var OpenAiImageProvider = class {
|
|
|
64723
64882
|
if (!res.ok) {
|
|
64724
64883
|
throw new ImageEditError(json2.error?.message ?? `OpenAI request failed (${res.status})`, this.id, res.status);
|
|
64725
64884
|
}
|
|
64726
|
-
const
|
|
64727
|
-
if (!
|
|
64885
|
+
const b642 = json2.data?.[0]?.b64_json;
|
|
64886
|
+
if (!b642)
|
|
64728
64887
|
throw new ImageEditError("OpenAI returned no image", this.id, res.status);
|
|
64729
|
-
return { bytes: fromBase64(
|
|
64888
|
+
return { bytes: fromBase64(b642), mime: "image/png" };
|
|
64730
64889
|
}
|
|
64731
64890
|
};
|
|
64732
64891
|
|
|
@@ -64802,7 +64961,7 @@ function createImageEditProvider(id, cfg2) {
|
|
|
64802
64961
|
// ../../packages/host-core/dist/image-gen-runner.js
|
|
64803
64962
|
function generatedImagesDir() {
|
|
64804
64963
|
const home = tesseractHome();
|
|
64805
|
-
return
|
|
64964
|
+
return join23(home, "generated-images");
|
|
64806
64965
|
}
|
|
64807
64966
|
function creds() {
|
|
64808
64967
|
return readDaemonAgentSettings().imageGen;
|
|
@@ -64854,11 +65013,11 @@ function createDaemonImageGenRunner() {
|
|
|
64854
65013
|
});
|
|
64855
65014
|
if (!images.length)
|
|
64856
65015
|
return { ok: false, error: "The provider returned no image." };
|
|
64857
|
-
const dir2 = req.outDir ??
|
|
65016
|
+
const dir2 = req.outDir ?? join23(generatedImagesDir(), randomUUID11());
|
|
64858
65017
|
mkdirSync9(dir2, { recursive: true });
|
|
64859
65018
|
const files = images.map((img, i) => {
|
|
64860
65019
|
const ext = img.mime.split("/")[1]?.split("+")[0] || "png";
|
|
64861
|
-
const path6 =
|
|
65020
|
+
const path6 = join23(dir2, `${i + 1}.${ext}`);
|
|
64862
65021
|
writeFileSync10(path6, img.bytes);
|
|
64863
65022
|
return { path: path6, mime: img.mime };
|
|
64864
65023
|
});
|
|
@@ -64908,8 +65067,8 @@ var VideoGenError = class extends Error {
|
|
|
64908
65067
|
function toBase642(bytes2) {
|
|
64909
65068
|
return Buffer.from(bytes2).toString("base64");
|
|
64910
65069
|
}
|
|
64911
|
-
function fromBase642(
|
|
64912
|
-
return new Uint8Array(Buffer.from(
|
|
65070
|
+
function fromBase642(b642) {
|
|
65071
|
+
return new Uint8Array(Buffer.from(b642, "base64"));
|
|
64913
65072
|
}
|
|
64914
65073
|
|
|
64915
65074
|
// ../../packages/video-core/dist/gemini.js
|
|
@@ -66270,11 +66429,11 @@ function createVideoProvider(id, cfg2) {
|
|
|
66270
66429
|
init_base_paths();
|
|
66271
66430
|
import { mkdirSync as mkdirSync10, readdirSync as readdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync11, rmSync as rmSync3 } from "node:fs";
|
|
66272
66431
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "node:fs/promises";
|
|
66273
|
-
import { dirname as dirname10, join as
|
|
66274
|
-
import { randomUUID as
|
|
66432
|
+
import { dirname as dirname10, join as join24 } from "node:path";
|
|
66433
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
66275
66434
|
function mediaJobsDir() {
|
|
66276
66435
|
const home = tesseractHome();
|
|
66277
|
-
return
|
|
66436
|
+
return join24(home, "media-jobs");
|
|
66278
66437
|
}
|
|
66279
66438
|
var TERMINAL = /* @__PURE__ */ new Set(["succeeded", "failed", "canceled"]);
|
|
66280
66439
|
var POLL_MS2 = 1e4;
|
|
@@ -66291,8 +66450,8 @@ var VideoJobRegistry = class {
|
|
|
66291
66450
|
/** Submit a spec and start tracking it. Returns the persisted record. */
|
|
66292
66451
|
async submit(provider, spec, meta4) {
|
|
66293
66452
|
const handle3 = await provider.submit(spec);
|
|
66294
|
-
const id =
|
|
66295
|
-
const outDir = meta4.outDir ??
|
|
66453
|
+
const id = randomUUID12();
|
|
66454
|
+
const outDir = meta4.outDir ?? join24(mediaJobsDir(), id);
|
|
66296
66455
|
const job = {
|
|
66297
66456
|
id,
|
|
66298
66457
|
sessionId: meta4.sessionId,
|
|
@@ -66305,7 +66464,7 @@ var VideoJobRegistry = class {
|
|
|
66305
66464
|
resolution: spec.resolution ?? "720p",
|
|
66306
66465
|
estimateUsd: meta4.estimateUsd,
|
|
66307
66466
|
state: "running",
|
|
66308
|
-
outPath:
|
|
66467
|
+
outPath: join24(outDir, "video.mp4"),
|
|
66309
66468
|
createdAt: Date.now(),
|
|
66310
66469
|
updatedAt: Date.now()
|
|
66311
66470
|
};
|
|
@@ -66319,7 +66478,7 @@ var VideoJobRegistry = class {
|
|
|
66319
66478
|
if (mem)
|
|
66320
66479
|
return { ...mem };
|
|
66321
66480
|
try {
|
|
66322
|
-
const raw = readFileSync14(
|
|
66481
|
+
const raw = readFileSync14(join24(mediaJobsDir(), `${jobId}.json`), "utf-8");
|
|
66323
66482
|
return JSON.parse(raw);
|
|
66324
66483
|
} catch {
|
|
66325
66484
|
return null;
|
|
@@ -66369,7 +66528,7 @@ var VideoJobRegistry = class {
|
|
|
66369
66528
|
}
|
|
66370
66529
|
const now2 = Date.now();
|
|
66371
66530
|
for (const f of files) {
|
|
66372
|
-
const fp =
|
|
66531
|
+
const fp = join24(mediaJobsDir(), f);
|
|
66373
66532
|
try {
|
|
66374
66533
|
const job = JSON.parse(readFileSync14(fp, "utf-8"));
|
|
66375
66534
|
if (TERMINAL.has(job.state)) {
|
|
@@ -66492,7 +66651,7 @@ var VideoJobRegistry = class {
|
|
|
66492
66651
|
this.jobs.set(job.id, job);
|
|
66493
66652
|
try {
|
|
66494
66653
|
mkdirSync10(mediaJobsDir(), { recursive: true });
|
|
66495
|
-
writeFileSync11(
|
|
66654
|
+
writeFileSync11(join24(mediaJobsDir(), `${job.id}.json`), JSON.stringify(job, null, 2));
|
|
66496
66655
|
} catch {
|
|
66497
66656
|
}
|
|
66498
66657
|
}
|
|
@@ -66646,15 +66805,15 @@ function createDaemonVideoGenRunner() {
|
|
|
66646
66805
|
init_base_paths();
|
|
66647
66806
|
import { existsSync as existsSync13, readFileSync as readFileSync20 } from "node:fs";
|
|
66648
66807
|
import { mkdir as mkdir6, writeFile as writeFile7 } from "node:fs/promises";
|
|
66649
|
-
import { join as
|
|
66650
|
-
import { randomUUID as
|
|
66808
|
+
import { join as join28, basename as basename4 } from "node:path";
|
|
66809
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
66651
66810
|
|
|
66652
66811
|
// ../../packages/host-core/dist/video-edit.js
|
|
66653
66812
|
init_base_paths();
|
|
66654
66813
|
import { spawn as spawn7 } from "node:child_process";
|
|
66655
66814
|
import { mkdirSync as mkdirSync11, readFileSync as readFileSync16, rmSync as rmSync4, writeFileSync as writeFileSync12 } from "node:fs";
|
|
66656
|
-
import { join as
|
|
66657
|
-
import { randomUUID as
|
|
66815
|
+
import { join as join25, isAbsolute as isAbsolute4 } from "node:path";
|
|
66816
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
66658
66817
|
|
|
66659
66818
|
// ../../packages/host-core/dist/ffmpeg.js
|
|
66660
66819
|
import { existsSync as existsSync11 } from "node:fs";
|
|
@@ -66718,9 +66877,9 @@ function requireAbsolutePath(p, label = "path") {
|
|
|
66718
66877
|
const raw = typeof p === "string" ? p.trim() : "";
|
|
66719
66878
|
if (!raw)
|
|
66720
66879
|
throw new Error(`${label} is required.`);
|
|
66721
|
-
const expanded = raw === "~" ? userHome() : raw.startsWith("~/") || raw.startsWith("~\\") ?
|
|
66880
|
+
const expanded = raw === "~" ? userHome() : raw.startsWith("~/") || raw.startsWith("~\\") ? join25(userHome(), raw.slice(2)) : raw;
|
|
66722
66881
|
if (!isAbsolute4(expanded)) {
|
|
66723
|
-
throw new Error(`${label} must be an ABSOLUTE path (got "${raw}"). A relative path would be resolved against the background service's own directory, not your project, so it could silently act on the wrong file. Pass the full path, e.g. ${
|
|
66882
|
+
throw new Error(`${label} must be an ABSOLUTE path (got "${raw}"). A relative path would be resolved against the background service's own directory, not your project, so it could silently act on the wrong file. Pass the full path, e.g. ${join25(userHome(), "Movies", "clip.mp4")}.`);
|
|
66724
66883
|
}
|
|
66725
66884
|
return expanded;
|
|
66726
66885
|
}
|
|
@@ -66734,7 +66893,7 @@ function mediaSpawnCwd() {
|
|
|
66734
66893
|
}
|
|
66735
66894
|
var workDir = () => {
|
|
66736
66895
|
const home = tesseractHome();
|
|
66737
|
-
const dir2 =
|
|
66896
|
+
const dir2 = join25(home, "media-work", randomUUID13());
|
|
66738
66897
|
mkdirSync11(dir2, { recursive: true });
|
|
66739
66898
|
return dir2;
|
|
66740
66899
|
};
|
|
@@ -66826,10 +66985,10 @@ async function renderEdl(edl, opts) {
|
|
|
66826
66985
|
const dir2 = opts.outDir ?? workDir();
|
|
66827
66986
|
mkdirSync11(dir2, { recursive: true });
|
|
66828
66987
|
const format = edl.output?.format ?? "mp4";
|
|
66829
|
-
const outPath =
|
|
66988
|
+
const outPath = join25(dir2, `edit.${format}`);
|
|
66830
66989
|
let assFile;
|
|
66831
66990
|
if (edl.captions?.cues.length) {
|
|
66832
|
-
assFile =
|
|
66991
|
+
assFile = join25(dir2, "captions.ass");
|
|
66833
66992
|
const W = edl.output?.width ?? 1920;
|
|
66834
66993
|
const H = edl.output?.height ?? 1080;
|
|
66835
66994
|
writeFileSync12(assFile, buildAssSubtitles(edl.captions.cues, edl.captions.style ?? "tiktok-bold", W, H));
|
|
@@ -66899,7 +67058,7 @@ async function enhanceVideo(path6, spec, opts) {
|
|
|
66899
67058
|
}
|
|
66900
67059
|
const dir2 = opts?.outDir ?? workDir();
|
|
66901
67060
|
mkdirSync11(dir2, { recursive: true });
|
|
66902
|
-
const outPath =
|
|
67061
|
+
const outPath = join25(dir2, "enhanced.mp4");
|
|
66903
67062
|
const audioArgs = probe.hasAudio ? ["-c:a", "copy"] : ["-an"];
|
|
66904
67063
|
const args = [
|
|
66905
67064
|
"-i",
|
|
@@ -66979,7 +67138,7 @@ async function extractFrames(path6, timestamps, outDir) {
|
|
|
66979
67138
|
mkdirSync11(dir2, { recursive: true });
|
|
66980
67139
|
const out = [];
|
|
66981
67140
|
for (const [i, ts] of timestamps.slice(0, 20).entries()) {
|
|
66982
|
-
const file2 =
|
|
67141
|
+
const file2 = join25(dir2, `frame-${i + 1}-${Math.round(ts * 1e3)}ms.jpg`);
|
|
66983
67142
|
const r = await run(resolveFfmpeg(), ["-y", "-ss", String(Math.max(0, ts)), "-i", path6, "-frames:v", "1", "-q:v", "2", file2]);
|
|
66984
67143
|
if (!r.ok)
|
|
66985
67144
|
throw new Error(`Frame extraction at ${ts}s failed: ${r.stderr.slice(-300)}`);
|
|
@@ -67012,7 +67171,7 @@ async function extractAudio(path6, format, outDir, opts) {
|
|
|
67012
67171
|
path6 = requireAbsolutePath(path6, "path");
|
|
67013
67172
|
const dir2 = outDir ?? workDir();
|
|
67014
67173
|
mkdirSync11(dir2, { recursive: true });
|
|
67015
|
-
const out =
|
|
67174
|
+
const out = join25(dir2, `audio.${format}`);
|
|
67016
67175
|
const codec2 = format === "mp3" ? ["-c:a", "libmp3lame", "-q:a", "2"] : format === "wav" ? ["-c:a", "pcm_s16le"] : ["-c:a", "aac", "-b:a", "192k"];
|
|
67017
67176
|
const gain = opts?.gainDb ? ["-af", `volume=${opts.gainDb}dB`] : [];
|
|
67018
67177
|
const r = await run(resolveFfmpeg(), ["-y", "-i", path6, "-vn", ...gain, ...codec2, out], { timeoutMs: LONG_TIMEOUT_MS });
|
|
@@ -67028,7 +67187,7 @@ async function isolateSpeakerAudio(path6, windows2, outDir) {
|
|
|
67028
67187
|
const enable = merged.map((w) => `between(t,${w.startSec},${w.endSec})`).join("+");
|
|
67029
67188
|
const dir2 = outDir ?? workDir();
|
|
67030
67189
|
mkdirSync11(dir2, { recursive: true });
|
|
67031
|
-
const out =
|
|
67190
|
+
const out = join25(dir2, "speaker.m4a");
|
|
67032
67191
|
const r = await run(resolveFfmpeg(), ["-y", "-i", path6, "-vn", "-af", `volume=enable='1-min(1,${enable})':volume=0`, "-c:a", "aac", "-b:a", "192k", out], {
|
|
67033
67192
|
timeoutMs: LONG_TIMEOUT_MS
|
|
67034
67193
|
});
|
|
@@ -67059,7 +67218,7 @@ async function extractAudioChunks(path6, opts) {
|
|
|
67059
67218
|
const chunks = [];
|
|
67060
67219
|
try {
|
|
67061
67220
|
for (let start3 = 0, i = 0; start3 < total; start3 += chunkSec, i++) {
|
|
67062
|
-
const out =
|
|
67221
|
+
const out = join25(chunkDir, `chunk-${i}.m4a`);
|
|
67063
67222
|
const len = Math.min(chunkSec + overlapSec, total - start3);
|
|
67064
67223
|
const r = await run(resolveFfmpeg(), ["-y", "-ss", String(start3), "-t", String(len), "-i", path6, "-vn", "-c:a", "aac", "-b:a", "64k", "-ac", "1", out], {
|
|
67065
67224
|
timeoutMs: LONG_TIMEOUT_MS
|
|
@@ -67088,7 +67247,7 @@ function readChunkBase64(path6) {
|
|
|
67088
67247
|
init_base_paths();
|
|
67089
67248
|
import { spawn as spawn8 } from "node:child_process";
|
|
67090
67249
|
import { existsSync as existsSync12, mkdirSync as mkdirSync12, readFileSync as readFileSync17, readdirSync as readdirSync5 } from "node:fs";
|
|
67091
|
-
import { join as
|
|
67250
|
+
import { join as join26, dirname as dirname11 } from "node:path";
|
|
67092
67251
|
var upscaleCwd;
|
|
67093
67252
|
function upscaleSpawnCwd() {
|
|
67094
67253
|
if (!upscaleCwd) {
|
|
@@ -67104,12 +67263,12 @@ function resolveRealesrgan() {
|
|
|
67104
67263
|
const candidates = [
|
|
67105
67264
|
envBin,
|
|
67106
67265
|
// Packaged app: extraResources → Resources/vendor/realesrgan/<plat>/…
|
|
67107
|
-
process.resourcesPath ?
|
|
67266
|
+
process.resourcesPath ? join26(process.resourcesPath, "vendor", "realesrgan", platDir, exe) : void 0
|
|
67108
67267
|
].filter((p) => !!p && existsSync12(p));
|
|
67109
67268
|
const bin = candidates[0] ?? fromPath2("realesrgan-ncnn-vulkan");
|
|
67110
67269
|
if (!bin)
|
|
67111
67270
|
return null;
|
|
67112
|
-
const models = process.env.TESSERACT_REALESRGAN_MODELS?.trim() ||
|
|
67271
|
+
const models = process.env.TESSERACT_REALESRGAN_MODELS?.trim() || join26(dirname11(bin), "models");
|
|
67113
67272
|
return existsSync12(models) ? { bin, models } : null;
|
|
67114
67273
|
}
|
|
67115
67274
|
function fromPath2(cmd) {
|
|
@@ -67132,8 +67291,8 @@ function installedRealesrModels() {
|
|
|
67132
67291
|
return [];
|
|
67133
67292
|
return REALESRGAN_MODELS.filter((m) => {
|
|
67134
67293
|
if (m.scales.length > 1)
|
|
67135
|
-
return m.scales.some((s) => existsSync12(
|
|
67136
|
-
return existsSync12(
|
|
67294
|
+
return m.scales.some((s) => existsSync12(join26(re.models, `${m.id}-x${s}.bin`)));
|
|
67295
|
+
return existsSync12(join26(re.models, `${m.id}.bin`));
|
|
67137
67296
|
}).map((m) => m.id);
|
|
67138
67297
|
}
|
|
67139
67298
|
function upscaleAdvert() {
|
|
@@ -67152,7 +67311,7 @@ async function realesrganGpuWorks() {
|
|
|
67152
67311
|
}
|
|
67153
67312
|
try {
|
|
67154
67313
|
const dir2 = workDir();
|
|
67155
|
-
const inP =
|
|
67314
|
+
const inP = join26(dir2, "p.png"), outP = join26(dir2, "p2.png");
|
|
67156
67315
|
const gen = await run(resolveFfmpeg(), ["-y", "-f", "lavfi", "-i", "testsrc=size=64x64:duration=1", "-frames:v", "1", inP], { timeoutMs: 15e3 });
|
|
67157
67316
|
if (!gen.ok || !existsSync12(inP)) {
|
|
67158
67317
|
gpuProbe = false;
|
|
@@ -67191,7 +67350,7 @@ async function upscaleCapabilities(opts) {
|
|
|
67191
67350
|
}
|
|
67192
67351
|
function resolveFalKey() {
|
|
67193
67352
|
const home = tesseractHome();
|
|
67194
|
-
const file2 =
|
|
67353
|
+
const file2 = join26(home, "daemon", "fal.key");
|
|
67195
67354
|
if (existsSync12(file2)) {
|
|
67196
67355
|
const k = readFileSync17(file2, "utf-8").trim();
|
|
67197
67356
|
if (k)
|
|
@@ -67239,19 +67398,19 @@ async function realesrganUpscale(path6, opts) {
|
|
|
67239
67398
|
const model = opts.model ?? "realesr-animevideov3";
|
|
67240
67399
|
const scale = opts.scale ?? 2;
|
|
67241
67400
|
const dir2 = opts.outDir ?? workDir();
|
|
67242
|
-
const fin =
|
|
67401
|
+
const fin = join26(dir2, "in"), fup = join26(dir2, "up");
|
|
67243
67402
|
mkdirSync12(fin, { recursive: true });
|
|
67244
67403
|
mkdirSync12(fup, { recursive: true });
|
|
67245
67404
|
const fps = p.fps ?? 30;
|
|
67246
67405
|
const src = path6;
|
|
67247
67406
|
let extractSrc = src;
|
|
67248
67407
|
if (opts.restore && Object.keys(opts.restore).length) {
|
|
67249
|
-
const cleaned = await enhanceVideo(src, { ...opts.restore, sharpen: opts.restore.sharpen ?? "none" }, { outDir:
|
|
67408
|
+
const cleaned = await enhanceVideo(src, { ...opts.restore, sharpen: opts.restore.sharpen ?? "none" }, { outDir: join26(dir2, "restore") }).catch(() => null);
|
|
67250
67409
|
if (cleaned)
|
|
67251
67410
|
extractSrc = cleaned.path;
|
|
67252
67411
|
}
|
|
67253
67412
|
opts.onProgress?.(2, "Extracting frames\u2026");
|
|
67254
|
-
const ext = await run(resolveFfmpeg(), ["-i", extractSrc, "-fps_mode", "passthrough",
|
|
67413
|
+
const ext = await run(resolveFfmpeg(), ["-i", extractSrc, "-fps_mode", "passthrough", join26(fin, "f%08d.png")], { timeoutMs: LONG });
|
|
67255
67414
|
if (!ext.ok)
|
|
67256
67415
|
throw new Error(`Frame extraction failed: ${ext.stderr.slice(-300)}`);
|
|
67257
67416
|
const nFrames = readdirSync5(fin).filter((f) => f.endsWith(".png")).length;
|
|
@@ -67277,14 +67436,14 @@ async function realesrganUpscale(path6, opts) {
|
|
|
67277
67436
|
});
|
|
67278
67437
|
});
|
|
67279
67438
|
opts.onProgress?.(96, "Reassembling\u2026");
|
|
67280
|
-
const outPath =
|
|
67439
|
+
const outPath = join26(dir2, "enhanced.mp4");
|
|
67281
67440
|
const vf = opts.targetHeight ? `scale=w=-2:h=${Math.round(opts.targetHeight / 2) * 2}:flags=lanczos` : "null";
|
|
67282
67441
|
const audio = p.hasAudio ? ["-i", src, "-map", "0:v:0", "-map", "1:a:0?", "-c:a", "copy"] : [];
|
|
67283
67442
|
const asm = await run(resolveFfmpeg(), [
|
|
67284
67443
|
"-r",
|
|
67285
67444
|
String(fps),
|
|
67286
67445
|
"-i",
|
|
67287
|
-
|
|
67446
|
+
join26(fup, "f%08d.png"),
|
|
67288
67447
|
...audio,
|
|
67289
67448
|
"-vf",
|
|
67290
67449
|
vf,
|
|
@@ -67336,7 +67495,7 @@ async function falTopazEnhance(path6, spec, opts) {
|
|
|
67336
67495
|
throw new Error(`Downloading the fal result failed (HTTP ${dl.status}).`);
|
|
67337
67496
|
const dir2 = opts.outDir ?? workDir();
|
|
67338
67497
|
mkdirSync12(dir2, { recursive: true });
|
|
67339
|
-
const outPath =
|
|
67498
|
+
const outPath = join26(dir2, "enhanced.mp4");
|
|
67340
67499
|
const { writeFileSync: writeFileSync29 } = await import("node:fs");
|
|
67341
67500
|
writeFileSync29(outPath, Buffer.from(await dl.arrayBuffer()));
|
|
67342
67501
|
const out = await probeMedia(outPath).catch(() => null);
|
|
@@ -67382,7 +67541,7 @@ async function smartEnhance(path6, args) {
|
|
|
67382
67541
|
|
|
67383
67542
|
// ../../packages/host-core/dist/video-effects.js
|
|
67384
67543
|
import { readFileSync as readFileSync18, mkdirSync as mkdirSync13, writeFileSync as writeFileSync13 } from "node:fs";
|
|
67385
|
-
import { join as
|
|
67544
|
+
import { join as join27, basename as basename3, extname } from "node:path";
|
|
67386
67545
|
var IMG_MIME = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", webp: "image/webp", heic: "image/heic" };
|
|
67387
67546
|
var LONG2 = 6e5;
|
|
67388
67547
|
function listEffects() {
|
|
@@ -67469,7 +67628,7 @@ async function applyEffect(effectId, inputs, opts) {
|
|
|
67469
67628
|
const dir2 = opts?.outDir ?? workDir();
|
|
67470
67629
|
mkdirSync13(dir2, { recursive: true });
|
|
67471
67630
|
const ext = media.isVideo ? "mp4" : extname(new URL(media.url).pathname).slice(1) || "png";
|
|
67472
|
-
const outPath =
|
|
67631
|
+
const outPath = join27(dir2, `${effectId}.${ext}`);
|
|
67473
67632
|
writeFileSync13(outPath, Buffer.from(await dl.arrayBuffer()));
|
|
67474
67633
|
opts?.onProgress?.(100, "Done");
|
|
67475
67634
|
return { path: outPath, mime: media.isVideo ? "video/mp4" : IMG_MIME[ext] ?? "image/png", isVideo: media.isVideo, effectId, label: def.label };
|
|
@@ -67495,7 +67654,7 @@ ${inputs.prompt}` : "");
|
|
|
67495
67654
|
const out = await runImageEdit(provider, { op: "prompt", imageB64: readFileSync18(imgPath).toString("base64"), mime: IMG_MIME[ext] ?? "image/jpeg", prompt });
|
|
67496
67655
|
const dir2 = opts?.outDir ?? workDir();
|
|
67497
67656
|
mkdirSync13(dir2, { recursive: true });
|
|
67498
|
-
const outPath =
|
|
67657
|
+
const outPath = join27(dir2, `${def.id}.png`);
|
|
67499
67658
|
writeFileSync13(outPath, Buffer.from(out.bytes));
|
|
67500
67659
|
opts?.onProgress?.(100, "Done");
|
|
67501
67660
|
return { path: outPath, mime: out.mime || "image/png", isVideo: false, effectId: def.id, label: def.label };
|
|
@@ -67620,13 +67779,13 @@ var MODEL = "gemini-3.5-flash";
|
|
|
67620
67779
|
var TIMEOUT_MS = 6e4;
|
|
67621
67780
|
async function reviewFrameGemini(framePath, intent, apiKey) {
|
|
67622
67781
|
try {
|
|
67623
|
-
const
|
|
67782
|
+
const b642 = readFileSync19(framePath).toString("base64");
|
|
67624
67783
|
const prompt = `This is a frame from an AI-generated video clip meant to show: "${intent}". Judge ONLY for obvious defects: melted/warped faces or hands, garbled text, broken anatomy, severe artifacts. Reply with STRICT JSON: {"acceptable": <true|false>, "note": "<\u226412 words, only if not acceptable>"}.`;
|
|
67625
67784
|
const res = await fetch(`${BASE_URL4}/models/${MODEL}:generateContent`, {
|
|
67626
67785
|
method: "POST",
|
|
67627
67786
|
headers: { "Content-Type": "application/json", "x-goog-api-key": apiKey },
|
|
67628
67787
|
body: JSON.stringify({
|
|
67629
|
-
contents: [{ role: "user", parts: [{ text: prompt }, { inlineData: { mimeType: "image/jpeg", data:
|
|
67788
|
+
contents: [{ role: "user", parts: [{ text: prompt }, { inlineData: { mimeType: "image/jpeg", data: b642 } }] }],
|
|
67630
67789
|
generationConfig: { temperature: 0, responseMimeType: "application/json" }
|
|
67631
67790
|
}),
|
|
67632
67791
|
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
@@ -67656,7 +67815,7 @@ function heygenKey() {
|
|
|
67656
67815
|
function readDaemonKeyFile(name) {
|
|
67657
67816
|
try {
|
|
67658
67817
|
const home = tesseractHome();
|
|
67659
|
-
const f =
|
|
67818
|
+
const f = join28(home, "daemon", name);
|
|
67660
67819
|
if (existsSync13(f)) {
|
|
67661
67820
|
const k = readFileSync20(f, "utf-8").trim();
|
|
67662
67821
|
if (k)
|
|
@@ -67670,7 +67829,7 @@ var HEYGEN_POLL_MS = 8e3;
|
|
|
67670
67829
|
var HEYGEN_MAX_MS = 8 * 6e4;
|
|
67671
67830
|
function musicDir() {
|
|
67672
67831
|
const home = tesseractHome();
|
|
67673
|
-
return
|
|
67832
|
+
return join28(home, "generated-music");
|
|
67674
67833
|
}
|
|
67675
67834
|
function audioMimeForPath(p) {
|
|
67676
67835
|
const lower = p.toLowerCase();
|
|
@@ -67786,10 +67945,10 @@ function createDaemonVideoEditRunner() {
|
|
|
67786
67945
|
return { ok: false, error: "Music generation needs an ElevenLabs key \u2014 ask the user to add one in Settings \u2192 Voice." };
|
|
67787
67946
|
try {
|
|
67788
67947
|
const music = await generateMusicElevenLabs({ prompt, durationSec }, { apiKey });
|
|
67789
|
-
const dir2 = outDir ??
|
|
67948
|
+
const dir2 = outDir ?? join28(musicDir(), randomUUID14());
|
|
67790
67949
|
await mkdir6(dir2, { recursive: true });
|
|
67791
67950
|
const ext = music.mime.includes("wav") ? "wav" : music.mime.includes("mp4") ? "m4a" : "mp3";
|
|
67792
|
-
const path6 =
|
|
67951
|
+
const path6 = join28(dir2, `music.${ext}`);
|
|
67793
67952
|
await writeFile7(path6, music.bytes);
|
|
67794
67953
|
return { ok: true, file: { path: path6, mime: music.mime } };
|
|
67795
67954
|
} catch (e) {
|
|
@@ -67857,9 +68016,9 @@ function createDaemonVideoEditRunner() {
|
|
|
67857
68016
|
if (!poll.videoUrl)
|
|
67858
68017
|
return { ok: false, error: "HeyGen reported the render complete but returned no video URL." };
|
|
67859
68018
|
const dl = await downloadHeyGenVideo(poll.videoUrl);
|
|
67860
|
-
const dir2 = outDir ??
|
|
68019
|
+
const dir2 = outDir ?? join28(musicDir(), randomUUID14());
|
|
67861
68020
|
await mkdir6(dir2, { recursive: true });
|
|
67862
|
-
const path6 =
|
|
68021
|
+
const path6 = join28(dir2, "avatar.mp4");
|
|
67863
68022
|
await writeFile7(path6, dl.bytes);
|
|
67864
68023
|
return { ok: true, file: { path: path6, mime: dl.mime } };
|
|
67865
68024
|
}
|
|
@@ -67877,9 +68036,9 @@ function createDaemonVideoEditRunner() {
|
|
|
67877
68036
|
return { ok: false, error: "Text-to-speech needs an ElevenLabs key (Settings \u2192 Voice)." };
|
|
67878
68037
|
try {
|
|
67879
68038
|
const audio = await synthesizeSpeechElevenLabs(text8, voiceId, { apiKey, stability: opts?.stability, similarityBoost: opts?.similarityBoost, speed: opts?.speed });
|
|
67880
|
-
const dir2 = outDir ??
|
|
68039
|
+
const dir2 = outDir ?? join28(musicDir(), randomUUID14());
|
|
67881
68040
|
await mkdir6(dir2, { recursive: true });
|
|
67882
|
-
const path6 =
|
|
68041
|
+
const path6 = join28(dir2, "speech.mp3");
|
|
67883
68042
|
await writeFile7(path6, audio.bytes);
|
|
67884
68043
|
return { ok: true, file: { path: path6, mime: audio.mime } };
|
|
67885
68044
|
} catch (e) {
|
|
@@ -67913,7 +68072,7 @@ function createDaemonVideoEditRunner() {
|
|
|
67913
68072
|
|
|
67914
68073
|
// ../../packages/host-core/dist/support-submit.js
|
|
67915
68074
|
import { openSync as openSync3, fstatSync, readSync as readSync2, closeSync as closeSync3 } from "node:fs";
|
|
67916
|
-
import { join as
|
|
68075
|
+
import { join as join29 } from "node:path";
|
|
67917
68076
|
|
|
67918
68077
|
// ../../packages/host-core/dist/host-metrics.js
|
|
67919
68078
|
import os from "node:os";
|
|
@@ -67976,7 +68135,7 @@ function createDaemonSupportSubmit() {
|
|
|
67976
68135
|
if (!creds3) {
|
|
67977
68136
|
throw new Error("Not signed in on this machine \u2014 sign in (or share this machine) to file a report from chat.");
|
|
67978
68137
|
}
|
|
67979
|
-
const daemonLog = tail2(
|
|
68138
|
+
const daemonLog = tail2(join29(daemonDir2(), "daemon.log"));
|
|
67980
68139
|
const diagnostics = {
|
|
67981
68140
|
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
67982
68141
|
source: "daemon",
|
|
@@ -68018,12 +68177,12 @@ function createDaemonSupportSubmit() {
|
|
|
68018
68177
|
init_dist4();
|
|
68019
68178
|
|
|
68020
68179
|
// ../../packages/host-core/dist/app-control-bridge.js
|
|
68021
|
-
import { randomUUID as
|
|
68180
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
68022
68181
|
var DEFAULT_APP_REQUEST_TIMEOUT_MS = 2e4;
|
|
68023
68182
|
var pending = /* @__PURE__ */ new Map();
|
|
68024
68183
|
function requestDesktopApp(emit2, origin, capabilityId, input, timeoutMs = DEFAULT_APP_REQUEST_TIMEOUT_MS, sessionId) {
|
|
68025
68184
|
return new Promise((resolve12, reject) => {
|
|
68026
|
-
const requestId = `app:${
|
|
68185
|
+
const requestId = `app:${randomUUID15()}`;
|
|
68027
68186
|
const timer = setTimeout(() => {
|
|
68028
68187
|
pending.delete(requestId);
|
|
68029
68188
|
reject(new Error("App request timed out \u2014 the Tesseract desktop app did not respond."));
|
|
@@ -68078,7 +68237,7 @@ var flushTimer = null;
|
|
|
68078
68237
|
var keepalive = null;
|
|
68079
68238
|
var gateTimer = null;
|
|
68080
68239
|
var stageGraceTimer = null;
|
|
68081
|
-
var
|
|
68240
|
+
var log4 = () => {
|
|
68082
68241
|
};
|
|
68083
68242
|
function snapshot() {
|
|
68084
68243
|
if (!state)
|
|
@@ -68179,7 +68338,7 @@ function attach(sub, hello) {
|
|
|
68179
68338
|
if (!accepted)
|
|
68180
68339
|
return;
|
|
68181
68340
|
subs.set(sub.deviceId, s);
|
|
68182
|
-
|
|
68341
|
+
log4(`[present] attach ${sub.role} ${sub.deviceId} (${sub.name})`);
|
|
68183
68342
|
if (sub.role === "stage" && stageGraceTimer) {
|
|
68184
68343
|
clearTimeout(stageGraceTimer);
|
|
68185
68344
|
stageGraceTimer = null;
|
|
@@ -68199,7 +68358,7 @@ function detach(deviceId2) {
|
|
|
68199
68358
|
if (!s)
|
|
68200
68359
|
return;
|
|
68201
68360
|
subs.delete(deviceId2);
|
|
68202
|
-
|
|
68361
|
+
log4(`[present] detach ${s.role} ${deviceId2}`);
|
|
68203
68362
|
if (s.role === "stage" && state) {
|
|
68204
68363
|
state.phase = "idle";
|
|
68205
68364
|
if (!stageGraceTimer) {
|
|
@@ -68263,7 +68422,7 @@ function start(msg, actorId) {
|
|
|
68263
68422
|
ensureKeepalive();
|
|
68264
68423
|
lastContextIndex = -1;
|
|
68265
68424
|
scheduleContext();
|
|
68266
|
-
|
|
68425
|
+
log4(`[present] start "${msg.deckTitle}" epoch=${epoch} slides=${slides.length}`);
|
|
68267
68426
|
for (const s of subs.values())
|
|
68268
68427
|
sendSlides(s);
|
|
68269
68428
|
bump();
|
|
@@ -68272,7 +68431,7 @@ function end(reason = "ended") {
|
|
|
68272
68431
|
if (!presentId)
|
|
68273
68432
|
return;
|
|
68274
68433
|
const id = presentId;
|
|
68275
|
-
|
|
68434
|
+
log4(`[present] end (${reason})`);
|
|
68276
68435
|
for (const s of subs.values()) {
|
|
68277
68436
|
try {
|
|
68278
68437
|
s.send({ type: "present:closed", presentId: id, reason });
|
|
@@ -68311,7 +68470,7 @@ function gotoIndex(index, by = "remote") {
|
|
|
68311
68470
|
state.build = 0;
|
|
68312
68471
|
scheduleContext();
|
|
68313
68472
|
}
|
|
68314
|
-
|
|
68473
|
+
log4(`[present] goto ${next} by=${by}`);
|
|
68315
68474
|
return bump();
|
|
68316
68475
|
}
|
|
68317
68476
|
function gotoSlideId(slideId, by = "remote") {
|
|
@@ -68367,7 +68526,7 @@ function setAiListening(listening, holdMs) {
|
|
|
68367
68526
|
gateTimer = setTimeout(() => {
|
|
68368
68527
|
gateTimer = null;
|
|
68369
68528
|
if (state?.ai.listening) {
|
|
68370
|
-
|
|
68529
|
+
log4("[present] AI input gate expired (dead-man) \u2014 closing");
|
|
68371
68530
|
state.ai.listening = false;
|
|
68372
68531
|
bump();
|
|
68373
68532
|
}
|
|
@@ -68478,7 +68637,7 @@ function onClientFrame(deviceId2, msg, send3, meta4) {
|
|
|
68478
68637
|
return;
|
|
68479
68638
|
}
|
|
68480
68639
|
if (!sub) {
|
|
68481
|
-
|
|
68640
|
+
log4(`[present] auto-attaching ${deviceId2} on ${msg.type} \u2014 no hello, protocol assumed v${PRESENT_PROTOCOL_VERSION}`);
|
|
68482
68641
|
attach({ deviceId: deviceId2, role: meta4?.role ?? "remote", name: meta4?.name ?? deviceId2, send: send3 }, {
|
|
68483
68642
|
presentId: msg.presentId ?? "",
|
|
68484
68643
|
protocolVersion: PRESENT_PROTOCOL_VERSION
|
|
@@ -68976,7 +69135,7 @@ function buildSharedBackend(engineId) {
|
|
|
68976
69135
|
},
|
|
68977
69136
|
resolver,
|
|
68978
69137
|
// Daemon plugins (review #3): operator-controlled <daemonDir>/plugins, env-var secrets.
|
|
68979
|
-
buildDaemonPluginLoader(
|
|
69138
|
+
buildDaemonPluginLoader(join30(daemonDir2(), "plugins"))
|
|
68980
69139
|
).then((b) => {
|
|
68981
69140
|
const sendMessage = b.sendMessage.bind(b);
|
|
68982
69141
|
b.sendMessage = (params) => {
|
|
@@ -69083,7 +69242,7 @@ function refreshDaemonScheduling(b) {
|
|
|
69083
69242
|
}
|
|
69084
69243
|
b.configureScheduling({
|
|
69085
69244
|
enabled,
|
|
69086
|
-
file:
|
|
69245
|
+
file: join30(dir2, "scheduled_tasks.json"),
|
|
69087
69246
|
// projectDir omitted ⇒ fires track the daemon's currently-open project (setConfig).
|
|
69088
69247
|
isOwner: schedulingLock ? () => schedulingLock.isOwner() : void 0
|
|
69089
69248
|
});
|
|
@@ -69102,14 +69261,14 @@ var VOICE_GATEWAY = process.env.CCB_VOICE_GATEWAY !== "0";
|
|
|
69102
69261
|
import { readdir as readdir3, readFile as readFile9, writeFile as writeFile8, mkdir as mkdir7, cp, rm, rename as renameAsync, stat as stat2 } from "node:fs/promises";
|
|
69103
69262
|
import { openSync as openSync4, readSync as readSync3, writeSync, closeSync as closeSync4, statSync as statSync5, existsSync as existsSync14, mkdtempSync, rmSync as rmSync5, readFileSync as readFileSync21, readdirSync as readdirSync6, watch, realpathSync as realpathSync3, constants as fsConstants } from "node:fs";
|
|
69104
69263
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
69105
|
-
import { basename as basename5, dirname as dirname12, join as
|
|
69264
|
+
import { basename as basename5, dirname as dirname12, join as join32, relative as relative4, resolve as resolve6, sep as sep5, isAbsolute as isAbsolute5 } from "node:path";
|
|
69106
69265
|
import { execFile as execFile3 } from "node:child_process";
|
|
69107
69266
|
import { promisify as promisify2 } from "node:util";
|
|
69108
69267
|
|
|
69109
69268
|
// ../../packages/host-core/dist/fs-stats.js
|
|
69110
69269
|
init_dist();
|
|
69111
69270
|
import { readdir as readdir2, lstat, readFile as readFile8 } from "node:fs/promises";
|
|
69112
|
-
import { join as
|
|
69271
|
+
import { join as join31 } from "node:path";
|
|
69113
69272
|
var TEXT_CAP = 5 * 1024 * 1024;
|
|
69114
69273
|
async function dirStats(root, opts) {
|
|
69115
69274
|
const start3 = Date.now();
|
|
@@ -69140,11 +69299,11 @@ async function dirStats(root, opts) {
|
|
|
69140
69299
|
if (opts?.excludeHeavy && STATS_HEAVY_DIRS.has(e.name))
|
|
69141
69300
|
continue;
|
|
69142
69301
|
dirCount++;
|
|
69143
|
-
stack.push(
|
|
69302
|
+
stack.push(join31(dir2, e.name));
|
|
69144
69303
|
} else if (e.isFile()) {
|
|
69145
69304
|
fileCount++;
|
|
69146
69305
|
try {
|
|
69147
|
-
totalBytes += (await lstat(
|
|
69306
|
+
totalBytes += (await lstat(join31(dir2, e.name))).size;
|
|
69148
69307
|
} catch {
|
|
69149
69308
|
}
|
|
69150
69309
|
}
|
|
@@ -69179,7 +69338,7 @@ async function codeStats(root) {
|
|
|
69179
69338
|
continue;
|
|
69180
69339
|
if (e.isDirectory()) {
|
|
69181
69340
|
if (!STATS_HEAVY_DIRS.has(e.name))
|
|
69182
|
-
stack.push(
|
|
69341
|
+
stack.push(join31(dir2, e.name));
|
|
69183
69342
|
continue;
|
|
69184
69343
|
}
|
|
69185
69344
|
if (!e.isFile())
|
|
@@ -69187,7 +69346,7 @@ async function codeStats(root) {
|
|
|
69187
69346
|
const lang = langForFilename(e.name);
|
|
69188
69347
|
if (!lang)
|
|
69189
69348
|
continue;
|
|
69190
|
-
const full =
|
|
69349
|
+
const full = join31(dir2, e.name);
|
|
69191
69350
|
let size = 0;
|
|
69192
69351
|
try {
|
|
69193
69352
|
size = (await lstat(full)).size;
|
|
@@ -69280,7 +69439,7 @@ async function listAllFilesUnder(dir2) {
|
|
|
69280
69439
|
return;
|
|
69281
69440
|
if (ent.name === ".DS_Store")
|
|
69282
69441
|
continue;
|
|
69283
|
-
const full =
|
|
69442
|
+
const full = join32(d, ent.name);
|
|
69284
69443
|
if (ent.isDirectory()) {
|
|
69285
69444
|
if (!LISTALL_SKIP_DIRS.has(ent.name))
|
|
69286
69445
|
await walk(full);
|
|
@@ -69397,18 +69556,18 @@ function resolveParent(raw, root) {
|
|
|
69397
69556
|
if (raw === "" || raw === "~")
|
|
69398
69557
|
return root;
|
|
69399
69558
|
if (raw.startsWith("~/"))
|
|
69400
|
-
return
|
|
69401
|
-
return isAbsolute5(raw) ? raw :
|
|
69559
|
+
return join32(root, raw.slice(2));
|
|
69560
|
+
return isAbsolute5(raw) ? raw : join32(root, raw);
|
|
69402
69561
|
}
|
|
69403
69562
|
function resolveUniqueName(dir2, name) {
|
|
69404
|
-
const direct =
|
|
69563
|
+
const direct = join32(dir2, name);
|
|
69405
69564
|
if (!existsSync14(direct))
|
|
69406
69565
|
return direct;
|
|
69407
69566
|
const dot = name.lastIndexOf(".");
|
|
69408
69567
|
const base = dot > 0 ? name.slice(0, dot) : name;
|
|
69409
69568
|
const ext = dot > 0 ? name.slice(dot) : "";
|
|
69410
69569
|
for (let i = 2; i < 1e4; i++) {
|
|
69411
|
-
const candidate =
|
|
69570
|
+
const candidate = join32(dir2, `${base} ${i}${ext}`);
|
|
69412
69571
|
if (!existsSync14(candidate))
|
|
69413
69572
|
return candidate;
|
|
69414
69573
|
}
|
|
@@ -69481,12 +69640,12 @@ async function renderThumbnailImpl(real, maxPx) {
|
|
|
69481
69640
|
return { error: "Thumbnails are only available on macOS hosts." };
|
|
69482
69641
|
let dir2 = null;
|
|
69483
69642
|
try {
|
|
69484
|
-
dir2 = mkdtempSync(
|
|
69643
|
+
dir2 = mkdtempSync(join32(tmpdir3(), "fp-thumb-"));
|
|
69485
69644
|
await execFileAsync("qlmanage", ["-t", "-s", String(maxPx), "-o", dir2, real], { timeout: 1e4, killSignal: "SIGKILL" }).catch(() => void 0);
|
|
69486
69645
|
const produced = readdirSync6(dir2).find((f) => f.toLowerCase().endsWith(".png"));
|
|
69487
69646
|
if (!produced)
|
|
69488
69647
|
return { error: "No thumbnail available." };
|
|
69489
|
-
return { png: readFileSync21(
|
|
69648
|
+
return { png: readFileSync21(join32(dir2, produced)).toString("base64") };
|
|
69490
69649
|
} catch (err2) {
|
|
69491
69650
|
return { error: String(err2.message) };
|
|
69492
69651
|
} finally {
|
|
@@ -69526,7 +69685,7 @@ var MAX_STAT_PATHS = 4096;
|
|
|
69526
69685
|
var SAFE_SEG = /^[A-Za-z0-9._-]+$/;
|
|
69527
69686
|
var TRASH_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
69528
69687
|
function pruneTrash(root) {
|
|
69529
|
-
const trashDir =
|
|
69688
|
+
const trashDir = join32(root, TRASH_DIR);
|
|
69530
69689
|
if (!existsSync14(trashDir))
|
|
69531
69690
|
return;
|
|
69532
69691
|
const cutoff = Date.now() - TRASH_RETENTION_MS;
|
|
@@ -69535,9 +69694,9 @@ function pruneTrash(root) {
|
|
|
69535
69694
|
if (!e.isDirectory())
|
|
69536
69695
|
continue;
|
|
69537
69696
|
try {
|
|
69538
|
-
const meta4 = JSON.parse(readFileSync21(
|
|
69697
|
+
const meta4 = JSON.parse(readFileSync21(join32(trashDir, e.name, ".fp-meta.json"), "utf-8"));
|
|
69539
69698
|
if ((meta4.deletedAt ?? 0) < cutoff)
|
|
69540
|
-
rmSync5(
|
|
69699
|
+
rmSync5(join32(trashDir, e.name), { recursive: true, force: true });
|
|
69541
69700
|
} catch {
|
|
69542
69701
|
}
|
|
69543
69702
|
}
|
|
@@ -69563,12 +69722,12 @@ async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
|
69563
69722
|
return entries.map((e) => {
|
|
69564
69723
|
let id = null;
|
|
69565
69724
|
try {
|
|
69566
|
-
id = statId(
|
|
69725
|
+
id = statId(join32(real, e.name));
|
|
69567
69726
|
} catch {
|
|
69568
69727
|
}
|
|
69569
69728
|
return {
|
|
69570
69729
|
name: e.name,
|
|
69571
|
-
path:
|
|
69730
|
+
path: join32(dir2, e.name),
|
|
69572
69731
|
isDir: e.isDirectory(),
|
|
69573
69732
|
ignored: ignored.has(e.name),
|
|
69574
69733
|
size: id?.size,
|
|
@@ -69735,7 +69894,7 @@ async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
|
69735
69894
|
const name = String(a[1] ?? "").trim();
|
|
69736
69895
|
if (!validName(name))
|
|
69737
69896
|
return { ok: false, error: "Invalid project name." };
|
|
69738
|
-
const target =
|
|
69897
|
+
const target = join32(parent, name);
|
|
69739
69898
|
if (!insideOk(target))
|
|
69740
69899
|
return { ok: false, error: "Refusing to create a project outside the shared root." };
|
|
69741
69900
|
if (existsSync14(target))
|
|
@@ -69753,7 +69912,7 @@ async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
|
69753
69912
|
const name = String(a[1] ?? "").trim();
|
|
69754
69913
|
if (!validName(name))
|
|
69755
69914
|
return { ok: false, error: "Invalid name." };
|
|
69756
|
-
const target =
|
|
69915
|
+
const target = join32(parent, name);
|
|
69757
69916
|
if (!insideOk(target))
|
|
69758
69917
|
return OUTSIDE("create");
|
|
69759
69918
|
if (existsSync14(target))
|
|
@@ -69775,7 +69934,7 @@ async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
|
69775
69934
|
return { ok: false, error: "Invalid name." };
|
|
69776
69935
|
if (!insideOk(oldPath))
|
|
69777
69936
|
return OUTSIDE("rename");
|
|
69778
|
-
const newPath =
|
|
69937
|
+
const newPath = join32(dirname12(oldPath), newName);
|
|
69779
69938
|
if (existsSync14(newPath))
|
|
69780
69939
|
return { ok: false, error: `A file named "${newName}" already exists.` };
|
|
69781
69940
|
try {
|
|
@@ -69809,19 +69968,19 @@ async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
|
69809
69968
|
try {
|
|
69810
69969
|
pruneTrash(root);
|
|
69811
69970
|
const id = `${Date.now().toString(36)}-${(trashSeq++).toString(36)}`;
|
|
69812
|
-
const itemDir =
|
|
69971
|
+
const itemDir = join32(root, TRASH_DIR, id);
|
|
69813
69972
|
await mkdir7(itemDir, { recursive: true });
|
|
69814
69973
|
const name = basename5(p);
|
|
69815
|
-
const trashedPath =
|
|
69974
|
+
const trashedPath = join32(itemDir, name);
|
|
69816
69975
|
await moveItem(p, trashedPath);
|
|
69817
|
-
await writeFile8(
|
|
69976
|
+
await writeFile8(join32(itemDir, ".fp-meta.json"), JSON.stringify({ originalPath: resolve6(p), name, deletedAt: Date.now() }));
|
|
69818
69977
|
return { ok: true, trashId: id, path: trashedPath };
|
|
69819
69978
|
} catch (err2) {
|
|
69820
69979
|
return { ok: false, error: String(err2.message) };
|
|
69821
69980
|
}
|
|
69822
69981
|
}
|
|
69823
69982
|
case "listTrash": {
|
|
69824
|
-
const trashDir =
|
|
69983
|
+
const trashDir = join32(root, TRASH_DIR);
|
|
69825
69984
|
if (!existsSync14(trashDir))
|
|
69826
69985
|
return [];
|
|
69827
69986
|
try {
|
|
@@ -69830,8 +69989,8 @@ async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
|
69830
69989
|
if (!e.isDirectory())
|
|
69831
69990
|
continue;
|
|
69832
69991
|
try {
|
|
69833
|
-
const meta4 = JSON.parse(readFileSync21(
|
|
69834
|
-
out.push({ trashId: e.name, name: meta4.name, path:
|
|
69992
|
+
const meta4 = JSON.parse(readFileSync21(join32(trashDir, e.name, ".fp-meta.json"), "utf-8"));
|
|
69993
|
+
out.push({ trashId: e.name, name: meta4.name, path: join32(trashDir, e.name, meta4.name), originalPath: meta4.originalPath, deletedAt: meta4.deletedAt });
|
|
69835
69994
|
} catch {
|
|
69836
69995
|
}
|
|
69837
69996
|
}
|
|
@@ -69844,14 +70003,14 @@ async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
|
69844
70003
|
const id = String(a[0] ?? "");
|
|
69845
70004
|
if (!SAFE_SEG.test(id))
|
|
69846
70005
|
return { ok: false, error: "Bad trash id." };
|
|
69847
|
-
const itemDir =
|
|
70006
|
+
const itemDir = join32(root, TRASH_DIR, id);
|
|
69848
70007
|
if (!existsSync14(itemDir))
|
|
69849
70008
|
return { ok: false, error: "Not in trash." };
|
|
69850
70009
|
try {
|
|
69851
|
-
const meta4 = JSON.parse(readFileSync21(
|
|
70010
|
+
const meta4 = JSON.parse(readFileSync21(join32(itemDir, ".fp-meta.json"), "utf-8"));
|
|
69852
70011
|
if (!validName(meta4.name))
|
|
69853
70012
|
return { ok: false, error: "Bad trashed name." };
|
|
69854
|
-
const src =
|
|
70013
|
+
const src = join32(itemDir, meta4.name);
|
|
69855
70014
|
let dest = meta4.originalPath;
|
|
69856
70015
|
if (!insideOk(dest))
|
|
69857
70016
|
return OUTSIDE("restore");
|
|
@@ -69945,7 +70104,7 @@ function watchRemoteRoot(root, key2, onChange) {
|
|
|
69945
70104
|
const rel = String(filename);
|
|
69946
70105
|
if (rel.split(sep5).some((seg) => IGNORED.has(seg)))
|
|
69947
70106
|
return;
|
|
69948
|
-
pending2.add(
|
|
70107
|
+
pending2.add(join32(resolved, rel));
|
|
69949
70108
|
if (timer)
|
|
69950
70109
|
clearTimeout(timer);
|
|
69951
70110
|
timer = setTimeout(() => {
|
|
@@ -70160,11 +70319,11 @@ function releaseDcClient(clientId) {
|
|
|
70160
70319
|
// ../../packages/host-core/dist/fs-transfer.js
|
|
70161
70320
|
import { readdir as readdir4, lstat as lstat2, mkdir as mkdir8, chmod, rm as rm2, readlink, symlink, stat as stat3 } from "node:fs/promises";
|
|
70162
70321
|
import { openSync as openSync5, readSync as readSync4, closeSync as closeSync5, existsSync as existsSync15, createWriteStream } from "node:fs";
|
|
70163
|
-
import { dirname as dirname13, join as
|
|
70322
|
+
import { basename as basename6, dirname as dirname13, join as join34, relative as relative6, sep as sep7 } from "node:path";
|
|
70164
70323
|
|
|
70165
70324
|
// ../../packages/host-core/dist/active-link/paths.js
|
|
70166
70325
|
init_base_paths();
|
|
70167
|
-
import { join as
|
|
70326
|
+
import { join as join33, relative as relative5, sep as sep6 } from "node:path";
|
|
70168
70327
|
function activeLinkEnabled() {
|
|
70169
70328
|
return process.env.CCB_ACTIVE_LINK !== "0";
|
|
70170
70329
|
}
|
|
@@ -70190,7 +70349,7 @@ function toPosixRel(root, abs) {
|
|
|
70190
70349
|
return relative5(root, abs).split(sep6).join("/");
|
|
70191
70350
|
}
|
|
70192
70351
|
function fromPosixRel(root, rel) {
|
|
70193
|
-
return
|
|
70352
|
+
return join33(root, rel.split("/").join(sep6));
|
|
70194
70353
|
}
|
|
70195
70354
|
function isExcludedAbs(abs, root) {
|
|
70196
70355
|
const rel = toPosixRel(root, abs);
|
|
@@ -70202,7 +70361,7 @@ function activeLinkRoot() {
|
|
|
70202
70361
|
return process.env.CCB_ACTIVE_LINK_DIR ?? tessPath("active-link");
|
|
70203
70362
|
}
|
|
70204
70363
|
function linkStateDir(linkId) {
|
|
70205
|
-
return
|
|
70364
|
+
return join33(activeLinkRoot(), linkId);
|
|
70206
70365
|
}
|
|
70207
70366
|
|
|
70208
70367
|
// ../../packages/host-core/dist/fs-transfer.js
|
|
@@ -70215,11 +70374,43 @@ function clearTimer(t) {
|
|
|
70215
70374
|
return null;
|
|
70216
70375
|
}
|
|
70217
70376
|
function uniqueDir(parent, name) {
|
|
70218
|
-
let candidate =
|
|
70377
|
+
let candidate = join34(parent, name);
|
|
70378
|
+
for (let i = 2; existsSync15(candidate) && i < 1e4; i++)
|
|
70379
|
+
candidate = join34(parent, `${name} ${i}`);
|
|
70380
|
+
return candidate;
|
|
70381
|
+
}
|
|
70382
|
+
function uniqueFile(parent, name) {
|
|
70383
|
+
const dot = name.lastIndexOf(".");
|
|
70384
|
+
const stem = dot > 0 ? name.slice(0, dot) : name;
|
|
70385
|
+
const ext = dot > 0 ? name.slice(dot) : "";
|
|
70386
|
+
let candidate = join34(parent, name);
|
|
70219
70387
|
for (let i = 2; existsSync15(candidate) && i < 1e4; i++)
|
|
70220
|
-
candidate =
|
|
70388
|
+
candidate = join34(parent, `${stem} ${i}${ext}`);
|
|
70221
70389
|
return candidate;
|
|
70222
70390
|
}
|
|
70391
|
+
var inboundObserver = null;
|
|
70392
|
+
function setInboundTransferObserver(cb) {
|
|
70393
|
+
inboundObserver = cb;
|
|
70394
|
+
}
|
|
70395
|
+
function notifyInbound(s, phase, transferId, extra) {
|
|
70396
|
+
if (!inboundObserver)
|
|
70397
|
+
return;
|
|
70398
|
+
try {
|
|
70399
|
+
inboundObserver({
|
|
70400
|
+
transferId,
|
|
70401
|
+
phase,
|
|
70402
|
+
fromHost: s.fromHost,
|
|
70403
|
+
destPath: s.singleFilePath ?? s.destRoot,
|
|
70404
|
+
singleFile: !!s.singleFilePath,
|
|
70405
|
+
totalFiles: s.totalFiles,
|
|
70406
|
+
totalBytes: s.totalBytes,
|
|
70407
|
+
filesDone: s.filesDone,
|
|
70408
|
+
bytesDone: s.bytesDone,
|
|
70409
|
+
...extra
|
|
70410
|
+
});
|
|
70411
|
+
} catch {
|
|
70412
|
+
}
|
|
70413
|
+
}
|
|
70223
70414
|
var producers = /* @__PURE__ */ new Map();
|
|
70224
70415
|
function failProducer(transferId, message) {
|
|
70225
70416
|
const s = producers.get(transferId);
|
|
@@ -70260,7 +70451,7 @@ async function buildManifest(root, followSymlinks, excludeHeavy) {
|
|
|
70260
70451
|
continue;
|
|
70261
70452
|
if (e.isDirectory() && excludeHeavy && HEAVY_DIRS.has(e.name))
|
|
70262
70453
|
continue;
|
|
70263
|
-
const abs =
|
|
70454
|
+
const abs = join34(dir2, e.name);
|
|
70264
70455
|
const rel = relative6(root, abs).split(sep7).join("/");
|
|
70265
70456
|
const st = await lstat2(abs).catch(() => null);
|
|
70266
70457
|
if (!st)
|
|
@@ -70285,15 +70476,30 @@ async function buildManifest(root, followSymlinks, excludeHeavy) {
|
|
|
70285
70476
|
}
|
|
70286
70477
|
}
|
|
70287
70478
|
}
|
|
70479
|
+
const rootSt = await lstat2(root);
|
|
70480
|
+
if (!rootSt.isDirectory()) {
|
|
70481
|
+
const name = basename6(root);
|
|
70482
|
+
if (rootSt.isSymbolicLink()) {
|
|
70483
|
+
if (!followSymlinks)
|
|
70484
|
+
return { dirs, files: [{ rel: name, mode: rootSt.mode, kind: "symlink", size: 0 }], totalBytes: 0, singleFile: true };
|
|
70485
|
+
const real = await stat3(root);
|
|
70486
|
+
if (real.isDirectory()) {
|
|
70487
|
+
await walk(root);
|
|
70488
|
+
return { dirs, files, totalBytes, singleFile: false };
|
|
70489
|
+
}
|
|
70490
|
+
return { dirs, files: [{ rel: name, mode: real.mode, kind: "file", size: real.size }], totalBytes: real.size, singleFile: true };
|
|
70491
|
+
}
|
|
70492
|
+
return { dirs, files: [{ rel: name, mode: rootSt.mode, kind: "file", size: rootSt.size }], totalBytes: rootSt.size, singleFile: true };
|
|
70493
|
+
}
|
|
70288
70494
|
await walk(root);
|
|
70289
|
-
return { dirs, files, totalBytes };
|
|
70495
|
+
return { dirs, files, totalBytes, singleFile: false };
|
|
70290
70496
|
}
|
|
70291
70497
|
async function startOutboundTransfer(msg, controllerClientId, ownHostId2, hostRoot, send3) {
|
|
70292
70498
|
const { transferId, toHost, srcPath } = msg;
|
|
70293
70499
|
const state2 = { toHost, controllerClientId, send: send3, sentBytes: 0, ackedBytes: 0, done: false, cancelled: false, wake: null, idle: null };
|
|
70294
70500
|
producers.set(transferId, state2);
|
|
70295
70501
|
if (!isInsideRoot(srcPath, hostRoot)) {
|
|
70296
|
-
failProducer(transferId, "Source
|
|
70502
|
+
failProducer(transferId, "Source is outside the shared root.");
|
|
70297
70503
|
return;
|
|
70298
70504
|
}
|
|
70299
70505
|
armProducerWatchdog(transferId);
|
|
@@ -70301,22 +70507,24 @@ async function startOutboundTransfer(msg, controllerClientId, ownHostId2, hostRo
|
|
|
70301
70507
|
try {
|
|
70302
70508
|
manifest = await buildManifest(srcPath, msg.followSymlinks, msg.excludeHeavy);
|
|
70303
70509
|
} catch (e) {
|
|
70304
|
-
failProducer(transferId, `Couldn't read the source
|
|
70510
|
+
failProducer(transferId, `Couldn't read the source: ${e.message}`);
|
|
70305
70511
|
return;
|
|
70306
70512
|
}
|
|
70307
70513
|
if (state2.cancelled) {
|
|
70308
70514
|
producers.delete(transferId);
|
|
70309
70515
|
return;
|
|
70310
70516
|
}
|
|
70517
|
+
const srcBase = manifest.singleFile ? dirname13(srcPath) : srcPath;
|
|
70311
70518
|
send3({
|
|
70312
70519
|
type: "fs:transferManifest",
|
|
70313
70520
|
transferId,
|
|
70314
70521
|
toHost,
|
|
70315
70522
|
fromHost: ownHostId2,
|
|
70316
70523
|
controllerClientId,
|
|
70317
|
-
destName: msg.destName,
|
|
70524
|
+
destName: manifest.singleFile ? msg.destName || basename6(srcPath) : msg.destName,
|
|
70318
70525
|
destParent: msg.destParent,
|
|
70319
70526
|
onCollision: msg.onCollision,
|
|
70527
|
+
singleFile: manifest.singleFile,
|
|
70320
70528
|
totalFiles: manifest.files.length,
|
|
70321
70529
|
totalBytes: manifest.totalBytes,
|
|
70322
70530
|
dirs: manifest.dirs
|
|
@@ -70327,11 +70535,11 @@ async function startOutboundTransfer(msg, controllerClientId, ownHostId2, hostRo
|
|
|
70327
70535
|
for (let fileIx = 0; fileIx < manifest.files.length && !state2.cancelled; fileIx++) {
|
|
70328
70536
|
const f = manifest.files[fileIx];
|
|
70329
70537
|
if (f.kind === "symlink") {
|
|
70330
|
-
const target = await readlink(
|
|
70538
|
+
const target = await readlink(join34(srcBase, f.rel.split("/").join(sep7))).catch(() => "");
|
|
70331
70539
|
send3({ type: "fs:transferChunk", transferId, toHost, fileIx, rel: f.rel, mode: f.mode, kind: "symlink", offset: 0, base64: Buffer.from(target, "utf-8").toString("base64"), eof: true }, { hostId: toHost });
|
|
70332
70540
|
continue;
|
|
70333
70541
|
}
|
|
70334
|
-
const abs =
|
|
70542
|
+
const abs = join34(srcBase, f.rel.split("/").join(sep7));
|
|
70335
70543
|
let fd = null;
|
|
70336
70544
|
try {
|
|
70337
70545
|
fd = openSync5(abs, "r");
|
|
@@ -70388,6 +70596,7 @@ function emitProgress(transferId, send3, currentFile) {
|
|
|
70388
70596
|
const s = consumers.get(transferId);
|
|
70389
70597
|
if (!s)
|
|
70390
70598
|
return;
|
|
70599
|
+
notifyInbound(s, "progress", transferId, { currentFile });
|
|
70391
70600
|
send3({ type: "fs:transferProgress", transferId, filesDone: s.filesDone, totalFiles: s.totalFiles, bytesDone: s.bytesDone, totalBytes: s.totalBytes, currentFile }, { clientId: s.controllerClientId });
|
|
70392
70601
|
}
|
|
70393
70602
|
function finishConsumer(transferId, send3) {
|
|
@@ -70395,7 +70604,8 @@ function finishConsumer(transferId, send3) {
|
|
|
70395
70604
|
if (!s)
|
|
70396
70605
|
return;
|
|
70397
70606
|
s.idle = clearTimer(s.idle);
|
|
70398
|
-
|
|
70607
|
+
notifyInbound(s, "done", transferId);
|
|
70608
|
+
send3({ type: "fs:transferDone", transferId, destRoot: s.singleFilePath ?? s.destRoot, filesWritten: s.filesDone, bytesWritten: s.bytesDone }, { clientId: s.controllerClientId });
|
|
70399
70609
|
consumers.delete(transferId);
|
|
70400
70610
|
}
|
|
70401
70611
|
async function abortConsumer(transferId) {
|
|
@@ -70416,13 +70626,20 @@ async function abortConsumer(transferId) {
|
|
|
70416
70626
|
await rm2(s.destRoot, { recursive: true, force: true });
|
|
70417
70627
|
} catch {
|
|
70418
70628
|
}
|
|
70629
|
+
} else if (s.singleFilePath && s.createdFile) {
|
|
70630
|
+
try {
|
|
70631
|
+
await rm2(s.singleFilePath, { force: true });
|
|
70632
|
+
} catch {
|
|
70633
|
+
}
|
|
70419
70634
|
}
|
|
70420
70635
|
consumers.delete(transferId);
|
|
70421
70636
|
}
|
|
70422
70637
|
function failConsumer(transferId, message, send3) {
|
|
70423
70638
|
const s = consumers.get(transferId);
|
|
70424
|
-
if (s)
|
|
70639
|
+
if (s) {
|
|
70640
|
+
notifyInbound(s, "error", transferId, { message });
|
|
70425
70641
|
send3({ type: "fs:transferError", transferId, where: "dest", message }, { clientId: s.controllerClientId });
|
|
70642
|
+
}
|
|
70426
70643
|
void abortConsumer(transferId);
|
|
70427
70644
|
}
|
|
70428
70645
|
function armConsumerWatchdog(transferId, send3) {
|
|
@@ -70438,12 +70655,56 @@ async function beginInboundTransfer(msg, hostRoot, send3) {
|
|
|
70438
70655
|
send3({ type: "fs:transferError", transferId, where: "dest", message }, { clientId: controllerClientId });
|
|
70439
70656
|
send3({ type: "fs:transferCancel", transferId, toHost: fromHost }, { hostId: fromHost });
|
|
70440
70657
|
};
|
|
70441
|
-
const parent = msg.destParent && isInsideRoot(msg.destParent, hostRoot) ? msg.destParent : hostRoot;
|
|
70658
|
+
const parent = canonicalForGuard(msg.destParent && isInsideRoot(msg.destParent, hostRoot) ? msg.destParent : hostRoot);
|
|
70442
70659
|
if (!msg.destName || msg.destName.includes("/") || msg.destName.includes("\\") || msg.destName.includes("..")) {
|
|
70443
70660
|
failNoState("Invalid destination name.");
|
|
70444
70661
|
return;
|
|
70445
70662
|
}
|
|
70446
|
-
|
|
70663
|
+
const register = (state2) => {
|
|
70664
|
+
consumers.set(transferId, {
|
|
70665
|
+
...state2,
|
|
70666
|
+
totalFiles: msg.totalFiles,
|
|
70667
|
+
totalBytes: msg.totalBytes,
|
|
70668
|
+
filesDone: 0,
|
|
70669
|
+
bytesDone: 0,
|
|
70670
|
+
curIx: -1,
|
|
70671
|
+
curStream: null,
|
|
70672
|
+
cancelled: false,
|
|
70673
|
+
lastProgress: 0,
|
|
70674
|
+
idle: null
|
|
70675
|
+
});
|
|
70676
|
+
armConsumerWatchdog(transferId, send3);
|
|
70677
|
+
const s = consumers.get(transferId);
|
|
70678
|
+
notifyInbound(s, "begin", transferId);
|
|
70679
|
+
emitProgress(transferId, send3, "");
|
|
70680
|
+
};
|
|
70681
|
+
if (msg.singleFile) {
|
|
70682
|
+
let destFile = join34(parent, msg.destName);
|
|
70683
|
+
let createdFile = true;
|
|
70684
|
+
if (existsSync15(destFile)) {
|
|
70685
|
+
if (msg.onCollision === "abort") {
|
|
70686
|
+
failNoState(`"${msg.destName}" already exists on the destination.`);
|
|
70687
|
+
return;
|
|
70688
|
+
}
|
|
70689
|
+
if (msg.onCollision === "suffix")
|
|
70690
|
+
destFile = uniqueFile(parent, msg.destName);
|
|
70691
|
+
else
|
|
70692
|
+
createdFile = false;
|
|
70693
|
+
}
|
|
70694
|
+
if (!isInsideRoot(destFile, hostRoot)) {
|
|
70695
|
+
failNoState("Destination is outside the shared root.");
|
|
70696
|
+
return;
|
|
70697
|
+
}
|
|
70698
|
+
register({ fromHost, controllerClientId, destRoot: parent, createdRoot: false, singleFilePath: destFile, createdFile });
|
|
70699
|
+
if (msg.totalFiles === 0) {
|
|
70700
|
+
finishConsumer(transferId, send3);
|
|
70701
|
+
return;
|
|
70702
|
+
}
|
|
70703
|
+
await mkdir8(parent, { recursive: true }).catch(() => {
|
|
70704
|
+
});
|
|
70705
|
+
return;
|
|
70706
|
+
}
|
|
70707
|
+
let destRoot = join34(parent, msg.destName);
|
|
70447
70708
|
let createdRoot = false;
|
|
70448
70709
|
if (existsSync15(destRoot)) {
|
|
70449
70710
|
if (msg.onCollision === "abort") {
|
|
@@ -70461,23 +70722,7 @@ async function beginInboundTransfer(msg, hostRoot, send3) {
|
|
|
70461
70722
|
failNoState("Destination is outside the shared root.");
|
|
70462
70723
|
return;
|
|
70463
70724
|
}
|
|
70464
|
-
|
|
70465
|
-
fromHost,
|
|
70466
|
-
controllerClientId,
|
|
70467
|
-
destRoot,
|
|
70468
|
-
createdRoot,
|
|
70469
|
-
totalFiles: msg.totalFiles,
|
|
70470
|
-
totalBytes: msg.totalBytes,
|
|
70471
|
-
filesDone: 0,
|
|
70472
|
-
bytesDone: 0,
|
|
70473
|
-
curIx: -1,
|
|
70474
|
-
curStream: null,
|
|
70475
|
-
cancelled: false,
|
|
70476
|
-
lastProgress: 0,
|
|
70477
|
-
idle: null
|
|
70478
|
-
});
|
|
70479
|
-
armConsumerWatchdog(transferId, send3);
|
|
70480
|
-
emitProgress(transferId, send3, "");
|
|
70725
|
+
register({ fromHost, controllerClientId, destRoot, createdRoot, singleFilePath: null, createdFile: false });
|
|
70481
70726
|
if (msg.totalFiles === 0) {
|
|
70482
70727
|
finishConsumer(transferId, send3);
|
|
70483
70728
|
return;
|
|
@@ -70485,7 +70730,7 @@ async function beginInboundTransfer(msg, hostRoot, send3) {
|
|
|
70485
70730
|
try {
|
|
70486
70731
|
await mkdir8(destRoot, { recursive: true });
|
|
70487
70732
|
for (const d of msg.dirs) {
|
|
70488
|
-
const abs =
|
|
70733
|
+
const abs = join34(destRoot, d.rel.split("/").join(sep7));
|
|
70489
70734
|
if (isInsideRoot(abs, destRoot))
|
|
70490
70735
|
await mkdir8(abs, { recursive: true }).catch(() => {
|
|
70491
70736
|
});
|
|
@@ -70499,15 +70744,15 @@ async function writeInboundChunk(msg, send3) {
|
|
|
70499
70744
|
if (!s || s.cancelled)
|
|
70500
70745
|
return;
|
|
70501
70746
|
armConsumerWatchdog(msg.transferId, send3);
|
|
70502
|
-
const abs =
|
|
70503
|
-
if (msg.rel.includes("..") || !isInsideRoot(abs, s.destRoot)) {
|
|
70747
|
+
const abs = s.singleFilePath ?? join34(s.destRoot, msg.rel.split("/").join(sep7));
|
|
70748
|
+
if (!s.singleFilePath && (msg.rel.includes("..") || !isInsideRoot(abs, s.destRoot))) {
|
|
70504
70749
|
failConsumer(msg.transferId, `Refusing a path outside the destination: ${msg.rel}`, send3);
|
|
70505
70750
|
return;
|
|
70506
70751
|
}
|
|
70507
70752
|
try {
|
|
70508
70753
|
if (msg.kind === "symlink") {
|
|
70509
70754
|
const target = Buffer.from(msg.base64, "base64").toString("utf-8");
|
|
70510
|
-
const resolved =
|
|
70755
|
+
const resolved = join34(dirname13(abs), target);
|
|
70511
70756
|
if (isInsideRoot(resolved, s.destRoot)) {
|
|
70512
70757
|
await mkdir8(dirname13(abs), { recursive: true }).catch(() => {
|
|
70513
70758
|
});
|
|
@@ -70594,7 +70839,7 @@ function cancelTransfersForClient(clientId, send3) {
|
|
|
70594
70839
|
|
|
70595
70840
|
// ../../packages/host-core/dist/peer-link/index.js
|
|
70596
70841
|
init_base_paths();
|
|
70597
|
-
import { join as
|
|
70842
|
+
import { join as join35 } from "node:path";
|
|
70598
70843
|
|
|
70599
70844
|
// ../../packages/host-core/dist/peer-link/manager.js
|
|
70600
70845
|
init_tls_direct();
|
|
@@ -70778,7 +71023,7 @@ var PeerLinkManager = class {
|
|
|
70778
71023
|
}
|
|
70779
71024
|
const socket2 = await q.makeQuicSocket(this.udpPort);
|
|
70780
71025
|
this.quicSocket = socket2;
|
|
70781
|
-
await q.startQuicServerOnSocket({ socket: socket2, identity, onStream: (
|
|
71026
|
+
await q.startQuicServerOnSocket({ socket: socket2, identity, onStream: (bs4) => this.acceptInbound(bs4, "punch") });
|
|
70782
71027
|
const refresh = async () => {
|
|
70783
71028
|
const found = [];
|
|
70784
71029
|
for (const { host, port } of resolveStunServers()) {
|
|
@@ -71097,7 +71342,7 @@ var PeerLinkManager = class {
|
|
|
71097
71342
|
var P2P_ENABLED = process.env.CCB_P2P !== "0";
|
|
71098
71343
|
function identityPaths() {
|
|
71099
71344
|
const dir2 = process.env.CCB_P2P_DIR ?? tesseractHome();
|
|
71100
|
-
return { certPath:
|
|
71345
|
+
return { certPath: join35(dir2, "peer-cert.pem"), keyPath: join35(dir2, "peer-key.pem") };
|
|
71101
71346
|
}
|
|
71102
71347
|
var manager = null;
|
|
71103
71348
|
function peerLinkManager() {
|
|
@@ -71114,7 +71359,7 @@ async function openPeerLink(peerHostId, ctx2) {
|
|
|
71114
71359
|
// ../../packages/host-core/dist/active-link/engine.js
|
|
71115
71360
|
import { lstat as lstat7, readlink as readlink4, rm as rm3, mkdir as mkdir10, symlink as symlink2, rename as rename2, utimes as utimes2, writeFile as writeFile9 } from "node:fs/promises";
|
|
71116
71361
|
import { existsSync as existsSync18 } from "node:fs";
|
|
71117
|
-
import { dirname as dirname18, join as
|
|
71362
|
+
import { dirname as dirname18, join as join40, resolve as resolve9 } from "node:path";
|
|
71118
71363
|
|
|
71119
71364
|
// ../../packages/host-core/dist/active-link/blocks.js
|
|
71120
71365
|
import { createHash as createHash4 } from "node:crypto";
|
|
@@ -71379,7 +71624,7 @@ var BlockAssembler = class {
|
|
|
71379
71624
|
|
|
71380
71625
|
// ../../packages/host-core/dist/active-link/manifest.js
|
|
71381
71626
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync16, existsSync as existsSync17, renameSync as renameSync6 } from "node:fs";
|
|
71382
|
-
import { join as
|
|
71627
|
+
import { join as join36 } from "node:path";
|
|
71383
71628
|
|
|
71384
71629
|
// ../../packages/host-core/dist/active-link/reconcile.js
|
|
71385
71630
|
var ABSENT = { kind: "absent", t: -Infinity, hash: "" };
|
|
@@ -71415,7 +71660,7 @@ var LinkManifest = class {
|
|
|
71415
71660
|
this.dir = dir2;
|
|
71416
71661
|
}
|
|
71417
71662
|
file() {
|
|
71418
|
-
return
|
|
71663
|
+
return join36(this.dir, "manifest.json");
|
|
71419
71664
|
}
|
|
71420
71665
|
load() {
|
|
71421
71666
|
try {
|
|
@@ -71517,7 +71762,7 @@ var LinkManifest = class {
|
|
|
71517
71762
|
|
|
71518
71763
|
// ../../packages/host-core/dist/active-link/scanner.js
|
|
71519
71764
|
import { readdir as readdir5, lstat as lstat3, readlink as readlink2 } from "node:fs/promises";
|
|
71520
|
-
import { join as
|
|
71765
|
+
import { join as join37, sep as sep8 } from "node:path";
|
|
71521
71766
|
async function walkRels(root, extraIgnore) {
|
|
71522
71767
|
const out = [];
|
|
71523
71768
|
async function rec(dir2) {
|
|
@@ -71532,7 +71777,7 @@ async function walkRels(root, extraIgnore) {
|
|
|
71532
71777
|
continue;
|
|
71533
71778
|
if (e.isDirectory() && HEAVY_DIRS.has(e.name))
|
|
71534
71779
|
continue;
|
|
71535
|
-
const abs =
|
|
71780
|
+
const abs = join37(dir2, e.name);
|
|
71536
71781
|
if (extraIgnore?.(abs))
|
|
71537
71782
|
continue;
|
|
71538
71783
|
if (e.isDirectory())
|
|
@@ -71571,7 +71816,7 @@ async function scanTree(root, prior, allow, extraIgnore) {
|
|
|
71571
71816
|
return { records };
|
|
71572
71817
|
}
|
|
71573
71818
|
function absOf(root, rel) {
|
|
71574
|
-
return
|
|
71819
|
+
return join37(root, rel.split("/").join(sep8));
|
|
71575
71820
|
}
|
|
71576
71821
|
|
|
71577
71822
|
// ../../packages/host-core/dist/active-link/gitignore.js
|
|
@@ -71941,10 +72186,10 @@ var ReaddirpStream = class extends Readable {
|
|
|
71941
72186
|
}
|
|
71942
72187
|
async _formatEntry(dirent, path6) {
|
|
71943
72188
|
let entry;
|
|
71944
|
-
const
|
|
72189
|
+
const basename12 = this._isDirent ? dirent.name : dirent;
|
|
71945
72190
|
try {
|
|
71946
|
-
const fullPath = presolve(pjoin(path6,
|
|
71947
|
-
entry = { path: prelative(this._root, fullPath), fullPath, basename:
|
|
72191
|
+
const fullPath = presolve(pjoin(path6, basename12));
|
|
72192
|
+
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename12 };
|
|
71948
72193
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
71949
72194
|
} catch (err2) {
|
|
71950
72195
|
this._onError(err2);
|
|
@@ -72483,9 +72728,9 @@ var NodeFsHandler = class {
|
|
|
72483
72728
|
_watchWithNodeFs(path6, listener) {
|
|
72484
72729
|
const opts = this.fsw.options;
|
|
72485
72730
|
const directory = sysPath.dirname(path6);
|
|
72486
|
-
const
|
|
72731
|
+
const basename12 = sysPath.basename(path6);
|
|
72487
72732
|
const parent = this.fsw._getWatchedDir(directory);
|
|
72488
|
-
parent.add(
|
|
72733
|
+
parent.add(basename12);
|
|
72489
72734
|
const absolutePath = sysPath.resolve(path6);
|
|
72490
72735
|
const options = {
|
|
72491
72736
|
persistent: opts.persistent
|
|
@@ -72495,7 +72740,7 @@ var NodeFsHandler = class {
|
|
|
72495
72740
|
let closer;
|
|
72496
72741
|
if (opts.usePolling) {
|
|
72497
72742
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
72498
|
-
options.interval = enableBin && isBinaryPath(
|
|
72743
|
+
options.interval = enableBin && isBinaryPath(basename12) ? opts.binaryInterval : opts.interval;
|
|
72499
72744
|
closer = setFsWatchFileListener(path6, absolutePath, options, {
|
|
72500
72745
|
listener,
|
|
72501
72746
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -72517,11 +72762,11 @@ var NodeFsHandler = class {
|
|
|
72517
72762
|
if (this.fsw.closed) {
|
|
72518
72763
|
return;
|
|
72519
72764
|
}
|
|
72520
|
-
const
|
|
72521
|
-
const
|
|
72522
|
-
const parent = this.fsw._getWatchedDir(
|
|
72765
|
+
const dirname28 = sysPath.dirname(file2);
|
|
72766
|
+
const basename12 = sysPath.basename(file2);
|
|
72767
|
+
const parent = this.fsw._getWatchedDir(dirname28);
|
|
72523
72768
|
let prevStats = stats;
|
|
72524
|
-
if (parent.has(
|
|
72769
|
+
if (parent.has(basename12))
|
|
72525
72770
|
return;
|
|
72526
72771
|
const listener = async (path6, newStats) => {
|
|
72527
72772
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
|
|
@@ -72546,9 +72791,9 @@ var NodeFsHandler = class {
|
|
|
72546
72791
|
prevStats = newStats2;
|
|
72547
72792
|
}
|
|
72548
72793
|
} catch (error51) {
|
|
72549
|
-
this.fsw._remove(
|
|
72794
|
+
this.fsw._remove(dirname28, basename12);
|
|
72550
72795
|
}
|
|
72551
|
-
} else if (parent.has(
|
|
72796
|
+
} else if (parent.has(basename12)) {
|
|
72552
72797
|
const at = newStats.atimeMs;
|
|
72553
72798
|
const mt = newStats.mtimeMs;
|
|
72554
72799
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -73596,7 +73841,7 @@ var ActiveLinkEngine = class {
|
|
|
73596
73841
|
}
|
|
73597
73842
|
async start() {
|
|
73598
73843
|
this.emit({ type: "status", linkId: this.opts.linkId, state: "scanning" });
|
|
73599
|
-
await rm3(
|
|
73844
|
+
await rm3(join40(this.stateDir, ".staging"), { recursive: true, force: true }).catch(() => {
|
|
73600
73845
|
});
|
|
73601
73846
|
this.manifest.load();
|
|
73602
73847
|
await this.reconcileScan();
|
|
@@ -74224,7 +74469,7 @@ var ActiveLinkEngine = class {
|
|
|
74224
74469
|
this.inboundWatchdog.clear();
|
|
74225
74470
|
}
|
|
74226
74471
|
stagingPath(rel, tag) {
|
|
74227
|
-
return
|
|
74472
|
+
return join40(this.stateDir, ".staging", `${hashString(rel)}.${tag}`);
|
|
74228
74473
|
}
|
|
74229
74474
|
pruneSuppress() {
|
|
74230
74475
|
const now2 = this.now();
|
|
@@ -74250,9 +74495,9 @@ var ActiveLinkEngine = class {
|
|
|
74250
74495
|
|
|
74251
74496
|
// ../../packages/host-core/dist/active-link/config.js
|
|
74252
74497
|
import { readFileSync as readFileSync24, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17, existsSync as existsSync19, renameSync as renameSync7, watch as watch3 } from "node:fs";
|
|
74253
|
-
import { join as
|
|
74498
|
+
import { join as join41 } from "node:path";
|
|
74254
74499
|
function configPath2() {
|
|
74255
|
-
return
|
|
74500
|
+
return join41(activeLinkRoot(), "links.json");
|
|
74256
74501
|
}
|
|
74257
74502
|
function loadLinkConfigs() {
|
|
74258
74503
|
try {
|
|
@@ -74289,7 +74534,7 @@ function saveLinkConfig(cfg2) {
|
|
|
74289
74534
|
writeAll(links);
|
|
74290
74535
|
}
|
|
74291
74536
|
function pendingPath() {
|
|
74292
|
-
return
|
|
74537
|
+
return join41(activeLinkRoot(), "pending-deprovisions.json");
|
|
74293
74538
|
}
|
|
74294
74539
|
function loadPendingDeprovisions() {
|
|
74295
74540
|
try {
|
|
@@ -74911,6 +75156,12 @@ function connectedFsHosts() {
|
|
|
74911
75156
|
const mine = ownHostId();
|
|
74912
75157
|
return serverHosts.filter((h) => h.online && h.hostId !== mine && !h.hostId.endsWith(`:${deviceId}`)).filter((h) => h.kind === "electron" || h.kind === "cloud").filter((h) => !h.caps || h.caps.includes("fs")).map((h) => ({ hostId: h.hostId, label: h.name || (h.kind === "cloud" ? "Cloud agent" : "Remote machine") }));
|
|
74913
75158
|
}
|
|
75159
|
+
function listHosts() {
|
|
75160
|
+
return presentedHosts();
|
|
75161
|
+
}
|
|
75162
|
+
function ownHost() {
|
|
75163
|
+
return ownHostId();
|
|
75164
|
+
}
|
|
74914
75165
|
function assertServerSelection() {
|
|
74915
75166
|
if (!socket || socket.readyState !== 1)
|
|
74916
75167
|
return;
|
|
@@ -75088,7 +75339,7 @@ function sendActiveLinkProvision(cfg2) {
|
|
|
75088
75339
|
type: "provision",
|
|
75089
75340
|
linkId: cfg2.linkId,
|
|
75090
75341
|
fromHost: ownHostId(),
|
|
75091
|
-
projectName: cfg2.remoteSubfolder ??
|
|
75342
|
+
projectName: cfg2.remoteSubfolder ?? basename9(cfg2.localRoot),
|
|
75092
75343
|
...cfg2.remoteParent ? { remoteParent: cfg2.remoteParent } : {},
|
|
75093
75344
|
...cfg2.remoteSubfolder ? { remoteSubfolder: cfg2.remoteSubfolder } : {},
|
|
75094
75345
|
...cfg2.workspaceLinkId ? { workspaceLinkId: cfg2.workspaceLinkId } : {}
|
|
@@ -75115,9 +75366,9 @@ function provisionActiveLinkMirror(payload) {
|
|
|
75115
75366
|
const remoteSubfolder = typeof payload.remoteSubfolder === "string" ? payload.remoteSubfolder : void 0;
|
|
75116
75367
|
let localRoot;
|
|
75117
75368
|
if (remoteParent && remoteSubfolder) {
|
|
75118
|
-
localRoot =
|
|
75369
|
+
localRoot = join42(remoteParent, safeFolderName(remoteSubfolder));
|
|
75119
75370
|
} else {
|
|
75120
|
-
localRoot =
|
|
75371
|
+
localRoot = join42(HOST_ROOT, "Tesseract Mirrors", safeFolderName(projectName));
|
|
75121
75372
|
}
|
|
75122
75373
|
if (loadLinkConfigs().some((c) => c.localRoot === localRoot && c.linkId !== linkId)) {
|
|
75123
75374
|
localRoot = `${localRoot}-${linkId.slice(0, 6)}`;
|
|
@@ -75630,6 +75881,7 @@ function registerAsHost() {
|
|
|
75630
75881
|
const name = kind === "cloud" ? "Cloud agent" : hostname3();
|
|
75631
75882
|
const caps = hostSessions ? ["agent", "fs", "term", "e2e"] : ["agent", "fs", "term"];
|
|
75632
75883
|
caps.push("rdesk");
|
|
75884
|
+
caps.push("xfer");
|
|
75633
75885
|
const upAdvert = upscaleAdvert();
|
|
75634
75886
|
if (upAdvert.gpu)
|
|
75635
75887
|
caps.push("gpu");
|
|
@@ -75703,6 +75955,10 @@ function connect() {
|
|
|
75703
75955
|
hooks?.onChat?.(env.payload);
|
|
75704
75956
|
return;
|
|
75705
75957
|
}
|
|
75958
|
+
if (channel === "drop") {
|
|
75959
|
+
hooks?.onDrop?.(env.payload);
|
|
75960
|
+
return;
|
|
75961
|
+
}
|
|
75706
75962
|
if (channel === "p2p") {
|
|
75707
75963
|
const m = env.payload;
|
|
75708
75964
|
for (const cb of [...p2pSubscribers])
|
|
@@ -76233,6 +76489,14 @@ var RdeskSessionManager = class {
|
|
|
76233
76489
|
this.audit({ event: "mute", sessionId, detail: `mic=${!!mute.mic} sys=${!!mute.systemAudio}` });
|
|
76234
76490
|
this.deps.sendControl(sessionId, s.viewer.clientId, { type: "rdesk:muted", mic: !!mute.mic, systemAudio: !!mute.systemAudio });
|
|
76235
76491
|
}
|
|
76492
|
+
/** Host operator → viewer: send an in-session chat line. No-op if the session is gone. */
|
|
76493
|
+
sendChat(sessionId, text8) {
|
|
76494
|
+
const s = this.sessions.get(sessionId);
|
|
76495
|
+
if (!s)
|
|
76496
|
+
return;
|
|
76497
|
+
this.deps.sendControl(sessionId, s.viewer.clientId, { type: "rdesk:chat", text: text8, from: "host", at: Date.now() });
|
|
76498
|
+
this.audit({ event: "chat", sessionId, detail: `host: ${text8.slice(0, 80)}` });
|
|
76499
|
+
}
|
|
76236
76500
|
/** Switch the captured display. */
|
|
76237
76501
|
async setMonitor(sessionId, monitorId) {
|
|
76238
76502
|
const s = this.sessions.get(sessionId);
|
|
@@ -76261,6 +76525,19 @@ var RdeskSessionManager = class {
|
|
|
76261
76525
|
return this.setSource(sessionId, msg.sourceId);
|
|
76262
76526
|
if (msg.type === "rdesk:send-keys")
|
|
76263
76527
|
return this.sendKeys(sessionId, msg.combo);
|
|
76528
|
+
if (msg.type === "rdesk:chat") {
|
|
76529
|
+
const s = this.sessions.get(sessionId);
|
|
76530
|
+
const text8 = msg.text.slice(0, 4e3);
|
|
76531
|
+
this.deps.onChat?.({ sessionId, viewerName: s?.viewer.name, text: text8 });
|
|
76532
|
+
this.audit({ event: "chat", sessionId, detail: text8.slice(0, 80) });
|
|
76533
|
+
return;
|
|
76534
|
+
}
|
|
76535
|
+
if (msg.type === "rdesk:av") {
|
|
76536
|
+
const s = this.sessions.get(sessionId);
|
|
76537
|
+
this.deps.onCall?.({ sessionId, on: msg.on, viewerName: s?.viewer.name });
|
|
76538
|
+
this.audit({ event: "call", sessionId, detail: msg.on ? "viewer joined call" : "viewer left call" });
|
|
76539
|
+
return;
|
|
76540
|
+
}
|
|
76264
76541
|
if (msg.type === "rdesk:set-av") {
|
|
76265
76542
|
if (typeof msg.systemAudio === "boolean")
|
|
76266
76543
|
this.sessions.get(sessionId)?.engine.setMute({ systemAudio: msg.systemAudio });
|
|
@@ -76290,7 +76567,7 @@ var RdeskSessionManager = class {
|
|
|
76290
76567
|
};
|
|
76291
76568
|
|
|
76292
76569
|
// ../../packages/host-core/dist/rdesk/publisher-window.js
|
|
76293
|
-
import { join as
|
|
76570
|
+
import { join as join43 } from "node:path";
|
|
76294
76571
|
var windows = /* @__PURE__ */ new Map();
|
|
76295
76572
|
var ipcInstalled = false;
|
|
76296
76573
|
function ensureIpc() {
|
|
@@ -76404,7 +76681,7 @@ var PublisherWindow = class {
|
|
|
76404
76681
|
throw new Error("Publisher requires the daemon GUI shell (no BrowserWindow).");
|
|
76405
76682
|
ensureIpc();
|
|
76406
76683
|
const sourceId = opts.sourceId ?? await sourceIdForMonitor(opts.monitorId ?? await this.primaryMonitorId());
|
|
76407
|
-
const
|
|
76684
|
+
const win3 = new el2.BrowserWindow({
|
|
76408
76685
|
width: 320,
|
|
76409
76686
|
height: 200,
|
|
76410
76687
|
show: false,
|
|
@@ -76416,13 +76693,13 @@ var PublisherWindow = class {
|
|
|
76416
76693
|
y: -32e3,
|
|
76417
76694
|
webPreferences: { preload: daemonPreloadPath(), contextIsolation: true, sandbox: false, backgroundThrottling: false }
|
|
76418
76695
|
});
|
|
76419
|
-
this.win =
|
|
76696
|
+
this.win = win3;
|
|
76420
76697
|
windows.set(this.sessionId, this);
|
|
76421
76698
|
try {
|
|
76422
|
-
|
|
76699
|
+
win3.setContentProtection(true);
|
|
76423
76700
|
} catch {
|
|
76424
76701
|
}
|
|
76425
|
-
await
|
|
76702
|
+
await win3.loadFile(join43(daemonUiDir(), "publisher.html"));
|
|
76426
76703
|
this.send("rdesk:pub:start", {
|
|
76427
76704
|
sessionId: this.sessionId,
|
|
76428
76705
|
sourceId,
|
|
@@ -76447,7 +76724,7 @@ var PublisherWindow = class {
|
|
|
76447
76724
|
if (!el2?.BrowserWindow)
|
|
76448
76725
|
throw new Error("Publisher requires the daemon GUI shell (no BrowserWindow).");
|
|
76449
76726
|
ensureIpc();
|
|
76450
|
-
const
|
|
76727
|
+
const win3 = new el2.BrowserWindow({
|
|
76451
76728
|
width: 320,
|
|
76452
76729
|
height: 200,
|
|
76453
76730
|
show: false,
|
|
@@ -76458,13 +76735,13 @@ var PublisherWindow = class {
|
|
|
76458
76735
|
y: -32e3,
|
|
76459
76736
|
webPreferences: { preload: daemonPreloadPath(), contextIsolation: true, sandbox: false, backgroundThrottling: false }
|
|
76460
76737
|
});
|
|
76461
|
-
this.win =
|
|
76738
|
+
this.win = win3;
|
|
76462
76739
|
windows.set(this.sessionId, this);
|
|
76463
76740
|
try {
|
|
76464
|
-
|
|
76741
|
+
win3.setContentProtection(true);
|
|
76465
76742
|
} catch {
|
|
76466
76743
|
}
|
|
76467
|
-
await
|
|
76744
|
+
await win3.loadFile(join43(daemonUiDir(), "publisher.html"));
|
|
76468
76745
|
this.send("rdesk:pub:start", {
|
|
76469
76746
|
sessionId: this.sessionId,
|
|
76470
76747
|
mode: "canvas",
|
|
@@ -78299,7 +78576,17 @@ var XAAD = utf8("tesseract-vault-export-v1");
|
|
|
78299
78576
|
var XJSON_AAD = utf8("tesseract-vault-encrypted-json-v1");
|
|
78300
78577
|
|
|
78301
78578
|
// ../../packages/host-core/dist/vault/store.js
|
|
78302
|
-
import { randomUUID as
|
|
78579
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
78580
|
+
var b64 = (u) => Buffer.from(u).toString("base64");
|
|
78581
|
+
var u82 = (s) => new Uint8Array(Buffer.from(s, "base64"));
|
|
78582
|
+
var isServerRecoverableMeta = (m) => !m.wrappedVaultKey;
|
|
78583
|
+
var metaFromWire = (w) => ({
|
|
78584
|
+
format: "TVK1",
|
|
78585
|
+
kdf: w.kdf,
|
|
78586
|
+
wrappedVaultKey: w.wrappedVaultKey,
|
|
78587
|
+
recoveryWrappedVaultKey: w.recoveryWrappedVaultKey ?? void 0,
|
|
78588
|
+
clientUpdatedAt: w.updatedAt
|
|
78589
|
+
});
|
|
78303
78590
|
var TOMBSTONE_TTL_MS = 180 * 24 * 60 * 60 * 1e3;
|
|
78304
78591
|
var VaultStore = class {
|
|
78305
78592
|
persistence;
|
|
@@ -78311,6 +78598,15 @@ var VaultStore = class {
|
|
|
78311
78598
|
// decrypted, only while unlocked
|
|
78312
78599
|
dirty = /* @__PURE__ */ new Set();
|
|
78313
78600
|
// item ids changed locally since last successful push
|
|
78601
|
+
// Mode 1: a no-password local vault whose random key is cached in the 0600 daemon dir (zero
|
|
78602
|
+
// friction; a fresh install starts with an empty usable vault). Upgraded via setMasterPassword,
|
|
78603
|
+
// or merged into the account + deleted on sign-in.
|
|
78604
|
+
deviceVault = false;
|
|
78605
|
+
// Mode 3: signed-in with NO master password. The key is cached locally (non-ZK by design → offline
|
|
78606
|
+
// auto-unlock) AND server-recoverable so any signed-in device auto-unlocks.
|
|
78607
|
+
serverRecoverable = false;
|
|
78608
|
+
// One-shot: on the mode-3 → mode-4 upgrade, tell the server to drop its recoverable key copy.
|
|
78609
|
+
pendingClearServerKey = false;
|
|
78314
78610
|
constructor(persistence, now2 = () => Date.now()) {
|
|
78315
78611
|
this.persistence = persistence;
|
|
78316
78612
|
this.now = now2;
|
|
@@ -78324,7 +78620,163 @@ var VaultStore = class {
|
|
|
78324
78620
|
for (const it of this.enc.values())
|
|
78325
78621
|
if (!it.deleted)
|
|
78326
78622
|
live++;
|
|
78327
|
-
return {
|
|
78623
|
+
return {
|
|
78624
|
+
exists: this.meta !== null || this.deviceVault,
|
|
78625
|
+
unlocked: this.vaultKey !== null,
|
|
78626
|
+
itemCount: live,
|
|
78627
|
+
deviceVault: this.deviceVault,
|
|
78628
|
+
serverRecoverable: this.serverRecoverable,
|
|
78629
|
+
hasMasterPassword: !!(this.meta && this.meta.wrappedVaultKey && !this.serverRecoverable)
|
|
78630
|
+
};
|
|
78631
|
+
}
|
|
78632
|
+
isDeviceVault() {
|
|
78633
|
+
return this.deviceVault;
|
|
78634
|
+
}
|
|
78635
|
+
isServerRecoverable() {
|
|
78636
|
+
return this.serverRecoverable;
|
|
78637
|
+
}
|
|
78638
|
+
/** For sync push: the raw key to hand the server to seal (mode 3), so other devices auto-unlock. */
|
|
78639
|
+
serverRecoverableKeyForPush() {
|
|
78640
|
+
return this.serverRecoverable && this.vaultKey ? b64(this.vaultKey) : null;
|
|
78641
|
+
}
|
|
78642
|
+
/** For sync push: consume the one-shot "clear the server's recoverable key" flag (mode-4 upgrade). */
|
|
78643
|
+
/** Peek the one-shot "clear the server's recoverable key" flag (mode-4 upgrade) WITHOUT consuming it. */
|
|
78644
|
+
hasPendingClearServerKey() {
|
|
78645
|
+
return this.pendingClearServerKey;
|
|
78646
|
+
}
|
|
78647
|
+
/** Consume the flag — call ONLY after the meta PUT carrying `serverRecoverableKey: null` succeeds. */
|
|
78648
|
+
clearPendingServerKeyFlag() {
|
|
78649
|
+
this.pendingClearServerKey = false;
|
|
78650
|
+
}
|
|
78651
|
+
/** On sign-out, drop an ACCOUNT vault's local state so this process-wide singleton can't leak one
|
|
78652
|
+
* user's vault into the next user's sync (the daemon isn't restarted on an in-process account
|
|
78653
|
+
* switch). The account's ciphertext + meta are safe on the server (re-pulled on the next sign-in),
|
|
78654
|
+
* and the mode-3 device key IS the account key, so it's cleared too. A machine-local NO-PASSWORD
|
|
78655
|
+
* device vault (mode 1) is NOT account-bound — it's left intact. The next ensureVault re-establishes
|
|
78656
|
+
* a fresh device vault from the now-empty state. */
|
|
78657
|
+
resetForSignOut() {
|
|
78658
|
+
if (this.deviceVault)
|
|
78659
|
+
return;
|
|
78660
|
+
this.meta = null;
|
|
78661
|
+
this.enc.clear();
|
|
78662
|
+
this.vaultKey = null;
|
|
78663
|
+
this.items = null;
|
|
78664
|
+
this.dirty.clear();
|
|
78665
|
+
this.serverRecoverable = false;
|
|
78666
|
+
this.pendingClearServerKey = false;
|
|
78667
|
+
this.persistence.clearDeviceKey();
|
|
78668
|
+
this.persist();
|
|
78669
|
+
}
|
|
78670
|
+
// ── zero-friction lifecycle (device vault / server-recoverable / merge) ────────────
|
|
78671
|
+
/** Call once on load: if no vault exists, AUTO-CREATE an empty no-password device vault (mode 1,
|
|
78672
|
+
* no prompt); if a mode-3 (server-recoverable) meta + cached key exist, auto-unlock. No-op when a
|
|
78673
|
+
* master-password vault exists (it stays locked for the unlock screen). Mirrors the extension. */
|
|
78674
|
+
async ensureVault() {
|
|
78675
|
+
const stored = this.persistence.loadDeviceKey();
|
|
78676
|
+
if (this.meta) {
|
|
78677
|
+
if (isServerRecoverableMeta(this.meta) && stored) {
|
|
78678
|
+
this.vaultKey = u82(stored);
|
|
78679
|
+
this.serverRecoverable = true;
|
|
78680
|
+
await this.decryptAll();
|
|
78681
|
+
}
|
|
78682
|
+
return;
|
|
78683
|
+
}
|
|
78684
|
+
if (stored) {
|
|
78685
|
+
this.vaultKey = u82(stored);
|
|
78686
|
+
} else {
|
|
78687
|
+
this.vaultKey = newVaultKey();
|
|
78688
|
+
this.persistence.saveDeviceKey(b64(this.vaultKey));
|
|
78689
|
+
this.items = /* @__PURE__ */ new Map();
|
|
78690
|
+
}
|
|
78691
|
+
this.deviceVault = true;
|
|
78692
|
+
if (this.enc.size)
|
|
78693
|
+
await this.decryptAll();
|
|
78694
|
+
else
|
|
78695
|
+
this.items = this.items ?? /* @__PURE__ */ new Map();
|
|
78696
|
+
}
|
|
78697
|
+
/** On sign-in, reconcile the local vault against the account snapshot:
|
|
78698
|
+
* - account has a master-password vault → `needsMasterPassword` (caller collects it → mergeIntoAccount);
|
|
78699
|
+
* - else establish mode 3 from the device/server key + fold local items in (auto-unlock, no prompt). */
|
|
78700
|
+
async reconcileSignIn(snap) {
|
|
78701
|
+
if (snap.meta && !snap.meta.serverKey && snap.meta.wrappedVaultKey) {
|
|
78702
|
+
return { status: "needsMasterPassword" };
|
|
78703
|
+
}
|
|
78704
|
+
const localItems = this.items ? [...this.items.values()] : [];
|
|
78705
|
+
const cached3 = this.persistence.loadDeviceKey();
|
|
78706
|
+
let key2;
|
|
78707
|
+
if (snap.meta?.serverKey)
|
|
78708
|
+
key2 = u82(snap.meta.serverKey);
|
|
78709
|
+
else if (this.vaultKey)
|
|
78710
|
+
key2 = this.vaultKey;
|
|
78711
|
+
else if (cached3)
|
|
78712
|
+
key2 = u82(cached3);
|
|
78713
|
+
else
|
|
78714
|
+
key2 = newVaultKey();
|
|
78715
|
+
this.vaultKey = key2;
|
|
78716
|
+
this.persistence.saveDeviceKey(b64(key2));
|
|
78717
|
+
this.serverRecoverable = true;
|
|
78718
|
+
this.deviceVault = false;
|
|
78719
|
+
this.meta = { format: "TVK1", kdf: defaultKdfParams(), wrappedVaultKey: "", recoveryWrappedVaultKey: void 0, clientUpdatedAt: this.now() };
|
|
78720
|
+
this.enc = new Map((snap.items ?? []).map((i) => [i.id, i]));
|
|
78721
|
+
for (const it of localItems) {
|
|
78722
|
+
this.enc.set(it.id, await encryptItem(key2, it));
|
|
78723
|
+
this.dirty.add(it.id);
|
|
78724
|
+
}
|
|
78725
|
+
await this.decryptAll();
|
|
78726
|
+
this.persist();
|
|
78727
|
+
return { status: "unlocked" };
|
|
78728
|
+
}
|
|
78729
|
+
/** Merge the local vault's items INTO the account vault: unlock it with the master password (or
|
|
78730
|
+
* create it if the account has none), re-encrypt local items under the account key, and drop the
|
|
78731
|
+
* device vault. Returns a recovery key only when a NEW account vault was created. */
|
|
78732
|
+
async reconcileMergeIntoAccount(password, snap) {
|
|
78733
|
+
const localItems = this.items ? [...this.items.values()] : [];
|
|
78734
|
+
let accountKey;
|
|
78735
|
+
let recoveryKey;
|
|
78736
|
+
if (snap.meta) {
|
|
78737
|
+
const accountMeta = metaFromWire(snap.meta);
|
|
78738
|
+
accountKey = await unlockVault(password, accountMeta);
|
|
78739
|
+
this.meta = accountMeta;
|
|
78740
|
+
} else {
|
|
78741
|
+
const created = await createVault(password);
|
|
78742
|
+
const rec = await createRecoveryKey(created.vaultKey);
|
|
78743
|
+
this.meta = { ...created.meta, recoveryWrappedVaultKey: rec.recoveryWrappedVaultKey, clientUpdatedAt: this.now() };
|
|
78744
|
+
accountKey = created.vaultKey;
|
|
78745
|
+
recoveryKey = rec.recoveryKey;
|
|
78746
|
+
}
|
|
78747
|
+
this.enc = new Map((snap.items ?? []).map((i) => [i.id, i]));
|
|
78748
|
+
for (const it of localItems) {
|
|
78749
|
+
this.enc.set(it.id, await encryptItem(accountKey, it));
|
|
78750
|
+
this.dirty.add(it.id);
|
|
78751
|
+
}
|
|
78752
|
+
this.vaultKey = accountKey;
|
|
78753
|
+
this.deviceVault = false;
|
|
78754
|
+
this.serverRecoverable = false;
|
|
78755
|
+
this.persistence.clearDeviceKey();
|
|
78756
|
+
await this.decryptAll();
|
|
78757
|
+
this.persist();
|
|
78758
|
+
return { recoveryKey };
|
|
78759
|
+
}
|
|
78760
|
+
/** Set a master password on a no-password vault (mode 1 → 2, or mode 3 → 4). Same key, re-wrapped;
|
|
78761
|
+
* items unchanged. Drops the local key cache (now zero-knowledge) and — if it was server-recoverable
|
|
78762
|
+
* — schedules clearing the server's recoverable key copy on the next sync. Returns a recovery key. */
|
|
78763
|
+
async setMasterPassword(password) {
|
|
78764
|
+
if (!this.vaultKey)
|
|
78765
|
+
throw new Error("Vault is locked.");
|
|
78766
|
+
if (this.meta && this.meta.wrappedVaultKey && !this.serverRecoverable)
|
|
78767
|
+
throw new Error("A master password is already set.");
|
|
78768
|
+
const kdf = defaultKdfParams();
|
|
78769
|
+
const wrappedVaultKey = await wrapVaultKey(deriveMasterKey(password, kdf), this.vaultKey);
|
|
78770
|
+
const rec = await createRecoveryKey(this.vaultKey);
|
|
78771
|
+
const wasServerRecoverable = this.serverRecoverable;
|
|
78772
|
+
this.meta = { format: "TVK1", kdf, wrappedVaultKey, recoveryWrappedVaultKey: rec.recoveryWrappedVaultKey, clientUpdatedAt: this.now() };
|
|
78773
|
+
this.deviceVault = false;
|
|
78774
|
+
this.serverRecoverable = false;
|
|
78775
|
+
this.persistence.clearDeviceKey();
|
|
78776
|
+
if (wasServerRecoverable)
|
|
78777
|
+
this.pendingClearServerKey = true;
|
|
78778
|
+
this.persist();
|
|
78779
|
+
return { recoveryKey: rec.recoveryKey };
|
|
78328
78780
|
}
|
|
78329
78781
|
isUnlocked() {
|
|
78330
78782
|
return this.vaultKey !== null;
|
|
@@ -78342,6 +78794,12 @@ var VaultStore = class {
|
|
|
78342
78794
|
throw new Error("No vault to unlock");
|
|
78343
78795
|
this.vaultKey = new Uint8Array(Buffer.from(base64Key, "base64"));
|
|
78344
78796
|
await this.decryptAll();
|
|
78797
|
+
const live = [...this.enc.values()].filter((e) => !e.deleted).length;
|
|
78798
|
+
if (live > 0 && (this.items?.size ?? 0) === 0) {
|
|
78799
|
+
this.vaultKey = null;
|
|
78800
|
+
this.items = null;
|
|
78801
|
+
throw new Error("Key did not match this vault");
|
|
78802
|
+
}
|
|
78345
78803
|
}
|
|
78346
78804
|
persist() {
|
|
78347
78805
|
this.persistence.save({ meta: this.meta, items: [...this.enc.values()] });
|
|
@@ -78386,7 +78844,8 @@ var VaultStore = class {
|
|
|
78386
78844
|
if (enc.deleted)
|
|
78387
78845
|
continue;
|
|
78388
78846
|
try {
|
|
78389
|
-
|
|
78847
|
+
const it = await decryptItem(key2, enc);
|
|
78848
|
+
map2.set(it.id, it);
|
|
78390
78849
|
} catch {
|
|
78391
78850
|
}
|
|
78392
78851
|
}
|
|
@@ -78419,7 +78878,7 @@ var VaultStore = class {
|
|
|
78419
78878
|
const existing = input.id ? items.get(input.id) : void 0;
|
|
78420
78879
|
const { id: _drop, name, folderId, favorite, reprompt, notes, tags, ...content } = input;
|
|
78421
78880
|
const item = {
|
|
78422
|
-
id: input.id ??
|
|
78881
|
+
id: input.id ?? randomUUID16(),
|
|
78423
78882
|
name,
|
|
78424
78883
|
folderId,
|
|
78425
78884
|
favorite,
|
|
@@ -78448,7 +78907,7 @@ var VaultStore = class {
|
|
|
78448
78907
|
const now2 = this.now();
|
|
78449
78908
|
for (const d of drafts) {
|
|
78450
78909
|
const { name, notes, favorite, tags, ...content } = d;
|
|
78451
|
-
const item = { id:
|
|
78910
|
+
const item = { id: randomUUID16(), name, notes, favorite, tags, createdAt: now2, updatedAt: now2, ...content };
|
|
78452
78911
|
items.set(item.id, item);
|
|
78453
78912
|
this.enc.set(item.id, await encryptItem(key2, item));
|
|
78454
78913
|
this.dirty.add(item.id);
|
|
@@ -78474,6 +78933,8 @@ var VaultStore = class {
|
|
|
78474
78933
|
}
|
|
78475
78934
|
/** Adopt a newer remote meta (e.g. a master-password change on another device). */
|
|
78476
78935
|
applyRemoteMeta(meta4) {
|
|
78936
|
+
if (this.deviceVault)
|
|
78937
|
+
return;
|
|
78477
78938
|
if (meta4 && (!this.meta || (meta4.clientUpdatedAt ?? 0) > (this.meta.clientUpdatedAt ?? 0))) {
|
|
78478
78939
|
this.meta = meta4;
|
|
78479
78940
|
this.persist();
|
|
@@ -78531,9 +78992,18 @@ var VaultSync = class {
|
|
|
78531
78992
|
this.getToken = getToken2;
|
|
78532
78993
|
this.debounceMs = debounceMs;
|
|
78533
78994
|
}
|
|
78995
|
+
/** A local NO-PASSWORD device vault (mode 1) has no account to sync with — its items are encrypted
|
|
78996
|
+
* under the on-disk device key, so pushing them would poison the account with undecryptable
|
|
78997
|
+
* ciphertext AND pulling the account meta would overwrite the device vault's null meta → orphan
|
|
78998
|
+
* the local items on the next lock/restart (the exact data-loss reconcileSignIn guards against).
|
|
78999
|
+
* Sign-in (reconcileSignIn/mergeIntoAccount) is what turns a device vault INTO an account vault;
|
|
79000
|
+
* until then, sync must stay OFF. Mirrors the extension/WebVaultClient `if (deviceVault) return`. */
|
|
79001
|
+
syncable() {
|
|
79002
|
+
return !!this.getToken() && !this.store.isDeviceVault();
|
|
79003
|
+
}
|
|
78534
79004
|
/** Call after any local mutation (upsert/remove/import/meta change). Debounced. */
|
|
78535
79005
|
markLocalChange() {
|
|
78536
|
-
if (!this.
|
|
79006
|
+
if (!this.syncable())
|
|
78537
79007
|
return;
|
|
78538
79008
|
if (this.timer)
|
|
78539
79009
|
clearTimeout(this.timer);
|
|
@@ -78543,13 +79013,19 @@ var VaultSync = class {
|
|
|
78543
79013
|
/** Once per signed-in session (deduped by token): pull + reconcile, then push. */
|
|
78544
79014
|
async ensureSynced() {
|
|
78545
79015
|
const token3 = this.getToken();
|
|
78546
|
-
if (!
|
|
79016
|
+
if (!this.syncable() || token3 === this.lastSyncedToken)
|
|
78547
79017
|
return;
|
|
78548
79018
|
this.lastSyncedToken = token3;
|
|
78549
79019
|
await this.pullAndReconcile().catch(() => {
|
|
78550
79020
|
this.lastSyncedToken = null;
|
|
78551
79021
|
});
|
|
78552
79022
|
}
|
|
79023
|
+
/** Push the current cache (dirty items + meta, incl. mode-3 serverRecoverableKey) NOW — used right
|
|
79024
|
+
* after a sign-in reconcile / merge so the account is updated immediately, not on the debounce. */
|
|
79025
|
+
async pushNow() {
|
|
79026
|
+
await this.push().catch(() => {
|
|
79027
|
+
});
|
|
79028
|
+
}
|
|
78553
79029
|
onSignedOut() {
|
|
78554
79030
|
this.lastSyncedToken = null;
|
|
78555
79031
|
this.lastMetaPushed = -1;
|
|
@@ -78573,7 +79049,7 @@ var VaultSync = class {
|
|
|
78573
79049
|
}
|
|
78574
79050
|
}
|
|
78575
79051
|
async push() {
|
|
78576
|
-
if (!this.
|
|
79052
|
+
if (!this.syncable())
|
|
78577
79053
|
return;
|
|
78578
79054
|
const dirty = this.store.takeDirty();
|
|
78579
79055
|
if (dirty.length) {
|
|
@@ -78583,8 +79059,17 @@ var VaultSync = class {
|
|
|
78583
79059
|
const { meta: meta4 } = this.store.metaForPush();
|
|
78584
79060
|
const clock = meta4?.clientUpdatedAt ?? -1;
|
|
78585
79061
|
if (meta4 && clock > this.lastMetaPushed) {
|
|
78586
|
-
|
|
79062
|
+
const wire = toWire(meta4);
|
|
79063
|
+
const srk = this.store.serverRecoverableKeyForPush();
|
|
79064
|
+
const clearing = !srk && this.store.hasPendingClearServerKey();
|
|
79065
|
+
if (srk)
|
|
79066
|
+
wire.serverRecoverableKey = srk;
|
|
79067
|
+
else if (clearing)
|
|
79068
|
+
wire.serverRecoverableKey = null;
|
|
79069
|
+
await this.api.putMeta(wire);
|
|
78587
79070
|
this.lastMetaPushed = clock;
|
|
79071
|
+
if (clearing)
|
|
79072
|
+
this.store.clearPendingServerKeyFlag();
|
|
78588
79073
|
}
|
|
78589
79074
|
}
|
|
78590
79075
|
};
|
|
@@ -78608,12 +79093,15 @@ function makeDaemonVaultApi(getOrigin, getToken2) {
|
|
|
78608
79093
|
|
|
78609
79094
|
// ../../packages/host-core/dist/vault/persistence.js
|
|
78610
79095
|
init_base_paths();
|
|
78611
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
|
|
79096
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync17, unlinkSync as unlinkSync2 } from "node:fs";
|
|
78612
79097
|
import { dirname as dirname19 } from "node:path";
|
|
78613
79098
|
function vaultStoreFile() {
|
|
78614
79099
|
return homePath(".tesseract", "vault", "vault.json");
|
|
78615
79100
|
}
|
|
78616
|
-
function
|
|
79101
|
+
function vaultDeviceKeyFile() {
|
|
79102
|
+
return homePath(".tesseract", "vault", "device-key");
|
|
79103
|
+
}
|
|
79104
|
+
function makeVaultPersistence(file2 = vaultStoreFile, deviceKeyFile = vaultDeviceKeyFile) {
|
|
78617
79105
|
return {
|
|
78618
79106
|
load() {
|
|
78619
79107
|
try {
|
|
@@ -78633,6 +79121,27 @@ function makeVaultPersistence(file2 = vaultStoreFile) {
|
|
|
78633
79121
|
writeFileSync17(file2(), JSON.stringify(state2), { mode: 384 });
|
|
78634
79122
|
} catch {
|
|
78635
79123
|
}
|
|
79124
|
+
},
|
|
79125
|
+
loadDeviceKey() {
|
|
79126
|
+
try {
|
|
79127
|
+
return existsSync21(deviceKeyFile()) ? readFileSync25(deviceKeyFile(), "utf8").trim() || null : null;
|
|
79128
|
+
} catch {
|
|
79129
|
+
return null;
|
|
79130
|
+
}
|
|
79131
|
+
},
|
|
79132
|
+
saveDeviceKey(base64Key) {
|
|
79133
|
+
try {
|
|
79134
|
+
mkdirSync19(dirname19(deviceKeyFile()), { recursive: true, mode: 448 });
|
|
79135
|
+
writeFileSync17(deviceKeyFile(), base64Key, { mode: 384 });
|
|
79136
|
+
} catch {
|
|
79137
|
+
}
|
|
79138
|
+
},
|
|
79139
|
+
clearDeviceKey() {
|
|
79140
|
+
try {
|
|
79141
|
+
if (existsSync21(deviceKeyFile()))
|
|
79142
|
+
unlinkSync2(deviceKeyFile());
|
|
79143
|
+
} catch {
|
|
79144
|
+
}
|
|
78636
79145
|
}
|
|
78637
79146
|
};
|
|
78638
79147
|
}
|
|
@@ -78640,7 +79149,7 @@ function makeVaultPersistence(file2 = vaultStoreFile) {
|
|
|
78640
79149
|
// ../../packages/host-core/dist/vault/biometric.js
|
|
78641
79150
|
init_base_paths();
|
|
78642
79151
|
import { existsSync as existsSync22, mkdirSync as mkdirSync20, readFileSync as readFileSync26, rmSync as rmSync6, writeFileSync as writeFileSync18 } from "node:fs";
|
|
78643
|
-
import { join as
|
|
79152
|
+
import { join as join44 } from "node:path";
|
|
78644
79153
|
async function electronBio() {
|
|
78645
79154
|
if (!electronModule())
|
|
78646
79155
|
return null;
|
|
@@ -78661,7 +79170,7 @@ function bioDir() {
|
|
|
78661
79170
|
return d;
|
|
78662
79171
|
}
|
|
78663
79172
|
function bioFile(id) {
|
|
78664
|
-
return
|
|
79173
|
+
return join44(bioDir(), `${String(id).replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64)}.enc`);
|
|
78665
79174
|
}
|
|
78666
79175
|
async function biometricAvailable() {
|
|
78667
79176
|
if (process.platform !== "darwin")
|
|
@@ -78709,13 +79218,15 @@ function settingsFile() {
|
|
|
78709
79218
|
var VaultManager = class {
|
|
78710
79219
|
store;
|
|
78711
79220
|
sync;
|
|
79221
|
+
api;
|
|
78712
79222
|
autoLockMinutes = 0;
|
|
78713
79223
|
idleTimer = null;
|
|
78714
79224
|
constructor() {
|
|
78715
79225
|
this.store = new VaultStore(makeVaultPersistence());
|
|
78716
|
-
this.
|
|
79226
|
+
this.api = makeDaemonVaultApi(() => readCredentials()?.apiOrigin ?? "", () => readCredentials()?.accessToken ?? null);
|
|
79227
|
+
this.sync = new VaultSync(this.store, this.api, () => readCredentials()?.accessToken ?? null);
|
|
78717
79228
|
this.autoLockMinutes = this.loadAutoLock();
|
|
78718
|
-
void this.sync.ensureSynced().catch(() => {
|
|
79229
|
+
void this.ensureVault().then(() => this.sync.ensureSynced()).catch(() => {
|
|
78719
79230
|
});
|
|
78720
79231
|
}
|
|
78721
79232
|
// ── auto-lock ────────────────────────────────────────────────────────────────────
|
|
@@ -78764,12 +79275,49 @@ var VaultManager = class {
|
|
|
78764
79275
|
exists: s.exists,
|
|
78765
79276
|
unlocked: s.unlocked,
|
|
78766
79277
|
itemCount: s.itemCount,
|
|
78767
|
-
hasMasterPassword: s.
|
|
79278
|
+
hasMasterPassword: !!s.hasMasterPassword,
|
|
79279
|
+
deviceVault: !!s.deviceVault,
|
|
79280
|
+
serverRecoverable: !!s.serverRecoverable,
|
|
78768
79281
|
biometricAvailable: await biometricAvailable(),
|
|
78769
79282
|
biometricEnrolled: id ? biometricEnrolled(id) : false,
|
|
78770
79283
|
autoLockMinutes: this.autoLockMinutes
|
|
78771
79284
|
};
|
|
78772
79285
|
}
|
|
79286
|
+
// ── zero-friction lifecycle (device vault / sign-in reconcile / merge) ─────────────
|
|
79287
|
+
/** Auto-create/auto-unlock the no-password device vault (mode 1) or auto-unlock mode 3. Idempotent;
|
|
79288
|
+
* called on construction + on the app's vault-tab mount. Mirrors the extension's ensureLoaded. */
|
|
79289
|
+
async ensureVault() {
|
|
79290
|
+
await this.store.ensureVault();
|
|
79291
|
+
if (this.store.isServerRecoverable())
|
|
79292
|
+
void this.sync.ensureSynced().catch(() => {
|
|
79293
|
+
});
|
|
79294
|
+
this.touch();
|
|
79295
|
+
}
|
|
79296
|
+
/** On sign-in: reconcile local ↔ account. `needsMasterPassword` → the app collects it + calls
|
|
79297
|
+
* mergeIntoAccount; `unlocked` → mode 3 established from the device key + local items folded in. */
|
|
79298
|
+
async signIn() {
|
|
79299
|
+
const snap = await this.api.getSnapshot();
|
|
79300
|
+
const r = await this.store.reconcileSignIn(snap);
|
|
79301
|
+
if (r.status === "unlocked")
|
|
79302
|
+
await this.sync.pushNow();
|
|
79303
|
+
this.touch();
|
|
79304
|
+
return r;
|
|
79305
|
+
}
|
|
79306
|
+
/** Merge the local vault into the account (unlock/create it with `masterPassword`), then sync. */
|
|
79307
|
+
async mergeIntoAccount(masterPassword) {
|
|
79308
|
+
const snap = await this.api.getSnapshot();
|
|
79309
|
+
const r = await this.store.reconcileMergeIntoAccount(masterPassword, snap);
|
|
79310
|
+
await this.sync.pushNow();
|
|
79311
|
+
this.touch();
|
|
79312
|
+
return r;
|
|
79313
|
+
}
|
|
79314
|
+
/** Optional upgrade: set a master password on a no-password vault (mode 1→2 or 3→4). */
|
|
79315
|
+
async setMasterPassword(masterPassword) {
|
|
79316
|
+
const r = await this.store.setMasterPassword(masterPassword);
|
|
79317
|
+
await this.sync.pushNow();
|
|
79318
|
+
this.touch();
|
|
79319
|
+
return r;
|
|
79320
|
+
}
|
|
78773
79321
|
// ── lifecycle ────────────────────────────────────────────────────────────────────
|
|
78774
79322
|
async create(masterPassword) {
|
|
78775
79323
|
const res = await this.store.create(masterPassword);
|
|
@@ -78820,6 +79368,20 @@ var VaultManager = class {
|
|
|
78820
79368
|
});
|
|
78821
79369
|
this.touch();
|
|
78822
79370
|
}
|
|
79371
|
+
// ── cross-device unlock (phone approve → desktop unlock) ───────────────────────────
|
|
79372
|
+
/** The (non-secret) vault crypto header for the QR-unlock payload — kdf + wrapped keys, NO raw key.
|
|
79373
|
+
* Returned to the renderer so it can render the QR; the raw key never leaves the daemon. */
|
|
79374
|
+
exportMetaWire() {
|
|
79375
|
+
const m = this.store.getMeta();
|
|
79376
|
+
return m ? { format: m.format, kdf: m.kdf, wrappedVaultKey: m.wrappedVaultKey, recoveryWrappedVaultKey: m.recoveryWrappedVaultKey ?? null, updatedAt: m.clientUpdatedAt ?? 0 } : null;
|
|
79377
|
+
}
|
|
79378
|
+
/** Adopt a raw vault key recovered cross-device (the phone approved + sealed it back over the relay). */
|
|
79379
|
+
async adoptKey(base64Key) {
|
|
79380
|
+
await this.store.adoptVaultKeyBase64(base64Key);
|
|
79381
|
+
void this.sync.ensureSynced().catch(() => {
|
|
79382
|
+
});
|
|
79383
|
+
this.touch();
|
|
79384
|
+
}
|
|
78823
79385
|
disableBiometric() {
|
|
78824
79386
|
const id = this.accountId();
|
|
78825
79387
|
if (id)
|
|
@@ -78966,6 +79528,9 @@ var VaultManager = class {
|
|
|
78966
79528
|
onSignedOut() {
|
|
78967
79529
|
this.sync.onSignedOut();
|
|
78968
79530
|
this.lock();
|
|
79531
|
+
this.store.resetForSignOut();
|
|
79532
|
+
void this.store.ensureVault().catch(() => {
|
|
79533
|
+
});
|
|
78969
79534
|
}
|
|
78970
79535
|
};
|
|
78971
79536
|
var singleton = null;
|
|
@@ -78996,7 +79561,7 @@ function originAllowed(origin) {
|
|
|
78996
79561
|
}
|
|
78997
79562
|
}
|
|
78998
79563
|
async function startLocalServer(opts) {
|
|
78999
|
-
const
|
|
79564
|
+
const log9 = opts.log ?? (() => {
|
|
79000
79565
|
});
|
|
79001
79566
|
const wss3 = new import_websocket_server.default({ host: "127.0.0.1", port: 0 });
|
|
79002
79567
|
await new Promise((resolve12, reject) => {
|
|
@@ -79005,7 +79570,7 @@ async function startLocalServer(opts) {
|
|
|
79005
79570
|
});
|
|
79006
79571
|
const addr = wss3.address();
|
|
79007
79572
|
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
79008
|
-
|
|
79573
|
+
log9(`local listener on 127.0.0.1:${port}`);
|
|
79009
79574
|
wss3.on("connection", (ws, req) => {
|
|
79010
79575
|
const url2 = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
79011
79576
|
const origin = req.headers.origin ?? void 0;
|
|
@@ -79365,6 +79930,19 @@ async function startLocalServer(opts) {
|
|
|
79365
79930
|
case "status":
|
|
79366
79931
|
result = await vm.status();
|
|
79367
79932
|
break;
|
|
79933
|
+
case "ensureVault":
|
|
79934
|
+
await vm.ensureVault();
|
|
79935
|
+
result = { ok: true };
|
|
79936
|
+
break;
|
|
79937
|
+
case "signIn":
|
|
79938
|
+
result = await vm.signIn();
|
|
79939
|
+
break;
|
|
79940
|
+
case "mergeIntoAccount":
|
|
79941
|
+
result = await vm.mergeIntoAccount(String(a[0]));
|
|
79942
|
+
break;
|
|
79943
|
+
case "setMasterPassword":
|
|
79944
|
+
result = await vm.setMasterPassword(String(a[0]));
|
|
79945
|
+
break;
|
|
79368
79946
|
case "create":
|
|
79369
79947
|
result = await vm.create(String(a[0]));
|
|
79370
79948
|
break;
|
|
@@ -79380,6 +79958,13 @@ async function startLocalServer(opts) {
|
|
|
79380
79958
|
await vm.unlockBiometric();
|
|
79381
79959
|
result = { ok: true };
|
|
79382
79960
|
break;
|
|
79961
|
+
case "exportMeta":
|
|
79962
|
+
result = vm.exportMetaWire();
|
|
79963
|
+
break;
|
|
79964
|
+
case "adoptKey":
|
|
79965
|
+
await vm.adoptKey(String(a[0]));
|
|
79966
|
+
result = { ok: true };
|
|
79967
|
+
break;
|
|
79383
79968
|
case "lock":
|
|
79384
79969
|
vm.lock();
|
|
79385
79970
|
result = { ok: true };
|
|
@@ -79482,18 +80067,18 @@ async function startLocalServer(opts) {
|
|
|
79482
80067
|
|
|
79483
80068
|
// ../../packages/host-core/dist/daemon-runtime.js
|
|
79484
80069
|
init_base_paths();
|
|
79485
|
-
import { readFileSync as readFileSync30, writeFileSync as writeFileSync25, existsSync as existsSync28, mkdirSync as mkdirSync26, unlinkSync as
|
|
80070
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync25, existsSync as existsSync28, mkdirSync as mkdirSync26, unlinkSync as unlinkSync4, chmodSync as chmodSync5 } from "node:fs";
|
|
79486
80071
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
79487
|
-
import { join as
|
|
80072
|
+
import { join as join54 } from "node:path";
|
|
79488
80073
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
79489
80074
|
import { hostname as hostname4 } from "node:os";
|
|
79490
80075
|
|
|
79491
80076
|
// ../../packages/host-core/dist/device-identity.js
|
|
79492
|
-
import { join as
|
|
80077
|
+
import { join as join45 } from "node:path";
|
|
79493
80078
|
import { readFileSync as readFileSync28, writeFileSync as writeFileSync20, existsSync as existsSync23, mkdirSync as mkdirSync22, renameSync as renameSync8, chmodSync as chmodSync3 } from "node:fs";
|
|
79494
80079
|
var cached2 = null;
|
|
79495
80080
|
function identityPath() {
|
|
79496
|
-
return
|
|
80081
|
+
return join45(daemonDir2(), "identity.json");
|
|
79497
80082
|
}
|
|
79498
80083
|
async function loadOrCreateDeviceIdentity() {
|
|
79499
80084
|
if (cached2)
|
|
@@ -79521,6 +80106,40 @@ async function loadOrCreateDeviceIdentity() {
|
|
|
79521
80106
|
cached2 = kp;
|
|
79522
80107
|
return kp;
|
|
79523
80108
|
}
|
|
80109
|
+
var sealingCached = null;
|
|
80110
|
+
function sealingPath() {
|
|
80111
|
+
return join45(daemonDir2(), "sealing.json");
|
|
80112
|
+
}
|
|
80113
|
+
var EC_DH = { name: "ECDH", namedCurve: "P-256" };
|
|
80114
|
+
async function loadOrCreateSealingKey() {
|
|
80115
|
+
if (sealingCached)
|
|
80116
|
+
return sealingCached;
|
|
80117
|
+
const { subtle: subtle4 } = globalThis.crypto;
|
|
80118
|
+
try {
|
|
80119
|
+
const stored = JSON.parse(readFileSync28(sealingPath(), "utf-8"));
|
|
80120
|
+
if (stored?.privateJwk && stored.publicRaw) {
|
|
80121
|
+
const privateKey = await subtle4.importKey("jwk", stored.privateJwk, EC_DH, true, ["deriveBits"]);
|
|
80122
|
+
sealingCached = { privateKey, publicRaw: unb64u(stored.publicRaw) };
|
|
80123
|
+
return sealingCached;
|
|
80124
|
+
}
|
|
80125
|
+
} catch {
|
|
80126
|
+
}
|
|
80127
|
+
const kp = await subtle4.generateKey(EC_DH, true, ["deriveBits"]);
|
|
80128
|
+
const publicRaw = new Uint8Array(await subtle4.exportKey("raw", kp.publicKey));
|
|
80129
|
+
const privateJwk = await subtle4.exportKey("jwk", kp.privateKey);
|
|
80130
|
+
const dir2 = daemonDir2();
|
|
80131
|
+
if (!existsSync23(dir2))
|
|
80132
|
+
mkdirSync22(dir2, { recursive: true, mode: 448 });
|
|
80133
|
+
const tmp = `${sealingPath()}.tmp`;
|
|
80134
|
+
writeFileSync20(tmp, JSON.stringify({ privateJwk, publicRaw: b64u(publicRaw), alg: "P256-ECDH" }, null, 2), { mode: 384 });
|
|
80135
|
+
renameSync8(tmp, sealingPath());
|
|
80136
|
+
try {
|
|
80137
|
+
chmodSync3(sealingPath(), 384);
|
|
80138
|
+
} catch {
|
|
80139
|
+
}
|
|
80140
|
+
sealingCached = { privateKey: kp.privateKey, publicRaw };
|
|
80141
|
+
return sealingCached;
|
|
80142
|
+
}
|
|
79524
80143
|
var DEVICES_BASE = (apiOrigin) => `${apiOrigin.replace(/\/$/, "")}/api/core/tesseract/devices`;
|
|
79525
80144
|
async function registerDeviceKey(opts) {
|
|
79526
80145
|
try {
|
|
@@ -79532,7 +80151,8 @@ async function registerDeviceKey(opts) {
|
|
|
79532
80151
|
identityPub: b64u(opts.publicRaw),
|
|
79533
80152
|
alg: "P256-ECDSA",
|
|
79534
80153
|
tlsFingerprint256: opts.tlsFingerprint256,
|
|
79535
|
-
label: opts.label
|
|
80154
|
+
label: opts.label,
|
|
80155
|
+
...opts.sealingPub ? { sealingPub: opts.sealingPub, sealingPubSig: opts.sealingPubSig } : {}
|
|
79536
80156
|
})
|
|
79537
80157
|
});
|
|
79538
80158
|
return r.ok;
|
|
@@ -79569,12 +80189,584 @@ async function getPeerIdentityPub(opts) {
|
|
|
79569
80189
|
}
|
|
79570
80190
|
}
|
|
79571
80191
|
|
|
80192
|
+
// ../../packages/host-core/dist/drops/receive-ui.js
|
|
80193
|
+
import { basename as basename10, dirname as dirname21 } from "node:path";
|
|
80194
|
+
var log5 = (...a) => console.log("[drops]", ...a);
|
|
80195
|
+
function formatBytes(n) {
|
|
80196
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
80197
|
+
return "0 B";
|
|
80198
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
80199
|
+
const i = Math.min(units.length - 1, Math.floor(Math.log(n) / Math.log(1024)));
|
|
80200
|
+
const v = n / 1024 ** i;
|
|
80201
|
+
return `${i === 0 ? v : v.toFixed(v < 10 ? 1 : 0)} ${units[i]}`;
|
|
80202
|
+
}
|
|
80203
|
+
function initDropReceiveUi(deps = {}) {
|
|
80204
|
+
const live = /* @__PURE__ */ new Map();
|
|
80205
|
+
const clearActivity = () => {
|
|
80206
|
+
if (live.size === 0)
|
|
80207
|
+
setTrayActivity(null);
|
|
80208
|
+
};
|
|
80209
|
+
const describe3 = (e) => {
|
|
80210
|
+
const from = deps.hostLabel?.(e.fromHost) ?? "another machine";
|
|
80211
|
+
const what = e.singleFile || e.totalFiles === 1 ? basename10(e.destPath) : `${e.totalFiles} items`;
|
|
80212
|
+
return `${what} from ${from}`;
|
|
80213
|
+
};
|
|
80214
|
+
setInboundTransferObserver((e) => {
|
|
80215
|
+
try {
|
|
80216
|
+
if (e.phase === "begin") {
|
|
80217
|
+
live.set(e.transferId, { destPath: e.destPath, singleFile: e.singleFile, totalFiles: e.totalFiles, lastTrayAt: 0 });
|
|
80218
|
+
setTrayActivity(`receiving ${e.totalFiles === 1 ? "1 file" : `${e.totalFiles} files`}`);
|
|
80219
|
+
return;
|
|
80220
|
+
}
|
|
80221
|
+
const l = live.get(e.transferId);
|
|
80222
|
+
if (e.phase === "progress") {
|
|
80223
|
+
if (!l)
|
|
80224
|
+
return;
|
|
80225
|
+
const now2 = Date.now();
|
|
80226
|
+
if (now2 - l.lastTrayAt < 1e3)
|
|
80227
|
+
return;
|
|
80228
|
+
l.lastTrayAt = now2;
|
|
80229
|
+
const pct = e.totalBytes > 0 ? Math.min(99, Math.floor(e.bytesDone / e.totalBytes * 100)) : 0;
|
|
80230
|
+
setTrayActivity(`receiving ${formatBytes(e.totalBytes)} (${pct}%)`);
|
|
80231
|
+
return;
|
|
80232
|
+
}
|
|
80233
|
+
live.delete(e.transferId);
|
|
80234
|
+
clearActivity();
|
|
80235
|
+
if (e.phase === "error") {
|
|
80236
|
+
log5(`transfer ${e.transferId} failed: ${e.message ?? "unknown error"}`);
|
|
80237
|
+
if (deps.appIsUp?.())
|
|
80238
|
+
return;
|
|
80239
|
+
daemonNotify({ title: "Couldn't receive files", body: e.message ?? "The transfer failed.", silent: true });
|
|
80240
|
+
return;
|
|
80241
|
+
}
|
|
80242
|
+
const destPath = e.destPath || l?.destPath || "";
|
|
80243
|
+
if (!destPath)
|
|
80244
|
+
return;
|
|
80245
|
+
log5(`received ${e.totalFiles} item(s), ${formatBytes(e.bytesDone)} \u2192 ${destPath}`);
|
|
80246
|
+
if (deps.appIsUp?.())
|
|
80247
|
+
return;
|
|
80248
|
+
const reveal = () => {
|
|
80249
|
+
const el2 = electronModule();
|
|
80250
|
+
if (!el2)
|
|
80251
|
+
return;
|
|
80252
|
+
try {
|
|
80253
|
+
el2.shell.showItemInFolder(destPath);
|
|
80254
|
+
} catch (err2) {
|
|
80255
|
+
log5("reveal failed:", err2 instanceof Error ? err2.message : String(err2));
|
|
80256
|
+
}
|
|
80257
|
+
};
|
|
80258
|
+
const open5 = () => {
|
|
80259
|
+
const el2 = electronModule();
|
|
80260
|
+
if (!el2)
|
|
80261
|
+
return;
|
|
80262
|
+
void el2.shell.openPath(destPath).catch(() => {
|
|
80263
|
+
});
|
|
80264
|
+
};
|
|
80265
|
+
daemonNotify({
|
|
80266
|
+
title: "Files received",
|
|
80267
|
+
body: `${describe3(e)} \xB7 ${formatBytes(e.bytesDone)}`,
|
|
80268
|
+
actions: [{ id: "reveal", label: "Show in Finder" }, { id: "open", label: "Open" }],
|
|
80269
|
+
onAction: (id) => id === "open" ? open5() : reveal(),
|
|
80270
|
+
// Clicking the banner body is the only reliable action on macOS (buttons need
|
|
80271
|
+
// an expanded banner and are suppressed under Focus), so make it do the useful
|
|
80272
|
+
// thing rather than nothing.
|
|
80273
|
+
onClick: reveal
|
|
80274
|
+
});
|
|
80275
|
+
} catch (err2) {
|
|
80276
|
+
log5("receive-ui error:", err2 instanceof Error ? err2.message : String(err2));
|
|
80277
|
+
}
|
|
80278
|
+
});
|
|
80279
|
+
}
|
|
80280
|
+
|
|
80281
|
+
// ../../packages/host-core/dist/drops/handler.js
|
|
80282
|
+
import { basename as basename11 } from "node:path";
|
|
80283
|
+
|
|
80284
|
+
// ../../packages/host-core/dist/drops/paths.js
|
|
80285
|
+
init_base_paths();
|
|
80286
|
+
import { mkdir as mkdir11 } from "node:fs/promises";
|
|
80287
|
+
import { join as join46 } from "node:path";
|
|
80288
|
+
|
|
80289
|
+
// ../../packages/host-core/dist/drops/names.js
|
|
80290
|
+
var WINDOWS_RESERVED = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
|
|
80291
|
+
var CONTROL = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
80292
|
+
var BIDI = /[\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
|
|
80293
|
+
var MAX_NAME_BYTES = 200;
|
|
80294
|
+
var MAX_EXT_BYTES = 32;
|
|
80295
|
+
var MAX_DEPTH = 32;
|
|
80296
|
+
var utf82 = new TextEncoder();
|
|
80297
|
+
var byteLen = (s) => utf82.encode(s).length;
|
|
80298
|
+
function capBytes(stem, ext, max) {
|
|
80299
|
+
if (byteLen(stem + ext) <= max)
|
|
80300
|
+
return stem + ext;
|
|
80301
|
+
const budget = Math.max(1, max - byteLen(ext));
|
|
80302
|
+
let out = "";
|
|
80303
|
+
let used = 0;
|
|
80304
|
+
for (const ch of stem) {
|
|
80305
|
+
const n = byteLen(ch);
|
|
80306
|
+
if (used + n > budget)
|
|
80307
|
+
break;
|
|
80308
|
+
out += ch;
|
|
80309
|
+
used += n;
|
|
80310
|
+
}
|
|
80311
|
+
return out + ext;
|
|
80312
|
+
}
|
|
80313
|
+
function sanitizeDropName(raw, fallback = "file") {
|
|
80314
|
+
let s = String(raw ?? "").split(/[/\\]/).pop() ?? "";
|
|
80315
|
+
s = s.replace(CONTROL, "").replace(BIDI, "").normalize("NFC");
|
|
80316
|
+
s = s.replace(/[ .]+$/g, "");
|
|
80317
|
+
s = s.replace(/^\.+/, "");
|
|
80318
|
+
s = s.trim();
|
|
80319
|
+
if (!s)
|
|
80320
|
+
return fallback;
|
|
80321
|
+
const dot = s.lastIndexOf(".");
|
|
80322
|
+
let stem = dot > 0 ? s.slice(0, dot) : s;
|
|
80323
|
+
let ext = dot > 0 ? s.slice(dot) : "";
|
|
80324
|
+
if (byteLen(ext) > MAX_EXT_BYTES) {
|
|
80325
|
+
stem = s;
|
|
80326
|
+
ext = "";
|
|
80327
|
+
}
|
|
80328
|
+
if (!stem)
|
|
80329
|
+
return fallback;
|
|
80330
|
+
if (WINDOWS_RESERVED.test(stem))
|
|
80331
|
+
stem = `_${stem}`;
|
|
80332
|
+
const out = capBytes(stem, ext, MAX_NAME_BYTES);
|
|
80333
|
+
return out || fallback;
|
|
80334
|
+
}
|
|
80335
|
+
function sanitizeDropRel(rel) {
|
|
80336
|
+
const parts = String(rel ?? "").split("/").filter((p) => p !== "");
|
|
80337
|
+
if (!parts.length || parts.length > MAX_DEPTH)
|
|
80338
|
+
return null;
|
|
80339
|
+
const out = [];
|
|
80340
|
+
for (const p of parts) {
|
|
80341
|
+
if (p === "." || p === "..")
|
|
80342
|
+
return null;
|
|
80343
|
+
const clean = sanitizeDropName(p, "");
|
|
80344
|
+
if (!clean)
|
|
80345
|
+
return null;
|
|
80346
|
+
out.push(clean);
|
|
80347
|
+
}
|
|
80348
|
+
return out.join("/");
|
|
80349
|
+
}
|
|
80350
|
+
async function uniqueDropPath(abs) {
|
|
80351
|
+
const { access } = await import("node:fs/promises");
|
|
80352
|
+
const exists2 = async (p) => access(p).then(() => true, () => false);
|
|
80353
|
+
if (!await exists2(abs))
|
|
80354
|
+
return abs;
|
|
80355
|
+
const dir2 = abs.slice(0, abs.length - basenameOf(abs).length);
|
|
80356
|
+
const name = basenameOf(abs);
|
|
80357
|
+
const dot = name.lastIndexOf(".");
|
|
80358
|
+
const stem = dot > 0 ? name.slice(0, dot) : name;
|
|
80359
|
+
const ext = dot > 0 ? name.slice(dot) : "";
|
|
80360
|
+
for (let i = 2; i < 1e4; i++) {
|
|
80361
|
+
const candidate = `${dir2}${stem} ${i}${ext}`;
|
|
80362
|
+
if (!await exists2(candidate))
|
|
80363
|
+
return candidate;
|
|
80364
|
+
}
|
|
80365
|
+
return `${dir2}${stem} ${Date.now()}${ext}`;
|
|
80366
|
+
}
|
|
80367
|
+
function basenameOf(p) {
|
|
80368
|
+
const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
|
|
80369
|
+
return i === -1 ? p : p.slice(i + 1);
|
|
80370
|
+
}
|
|
80371
|
+
|
|
80372
|
+
// ../../packages/host-core/dist/drops/paths.js
|
|
80373
|
+
function resolveDropsRoot() {
|
|
80374
|
+
const root = resolveHostRoot();
|
|
80375
|
+
const preferred = dropsDir();
|
|
80376
|
+
return isInsideRoot(preferred, root) ? preferred : join46(root, "Drops");
|
|
80377
|
+
}
|
|
80378
|
+
async function ensureDropsRoot() {
|
|
80379
|
+
const dir2 = resolveDropsRoot();
|
|
80380
|
+
await mkdir11(dir2, { recursive: true });
|
|
80381
|
+
return dir2;
|
|
80382
|
+
}
|
|
80383
|
+
async function ensureDropDir(opts) {
|
|
80384
|
+
const root = await ensureDropsRoot();
|
|
80385
|
+
if (opts.singleItem && !opts.sender)
|
|
80386
|
+
return root;
|
|
80387
|
+
const parts = [root];
|
|
80388
|
+
if (opts.sender)
|
|
80389
|
+
parts.push(sanitizeDropName(opts.sender, "Unknown sender"));
|
|
80390
|
+
if (!opts.singleItem && opts.label)
|
|
80391
|
+
parts.push(sanitizeDropName(opts.label, "Files"));
|
|
80392
|
+
const dir2 = join46(...parts);
|
|
80393
|
+
await mkdir11(dir2, { recursive: true });
|
|
80394
|
+
return dir2;
|
|
80395
|
+
}
|
|
80396
|
+
|
|
80397
|
+
// ../../packages/host-core/dist/drops/handler.js
|
|
80398
|
+
var log6 = (...a) => console.log("[drops]", ...a);
|
|
80399
|
+
var CONSENT_TIMEOUT_MS = 12e4;
|
|
80400
|
+
function createDropHandler(deps) {
|
|
80401
|
+
const pending2 = /* @__PURE__ */ new Map();
|
|
80402
|
+
const seen = /* @__PURE__ */ new Set();
|
|
80403
|
+
async function claim(offer) {
|
|
80404
|
+
const hostId = deps.ownHostId();
|
|
80405
|
+
if (!hostId) {
|
|
80406
|
+
log6(`drop ${offer.dropId}: no hostId yet, leaving it to another device`);
|
|
80407
|
+
return;
|
|
80408
|
+
}
|
|
80409
|
+
const singleItem = offer.totalFiles === 1;
|
|
80410
|
+
const destDir = await ensureDropDir({
|
|
80411
|
+
singleItem,
|
|
80412
|
+
// Cross-user drops are grouped by sender so "delete everything they sent me" is
|
|
80413
|
+
// one action. Safe only because the name is sanitized on the way in.
|
|
80414
|
+
sender: offer.kind === "peer" ? offer.from.email || offer.from.name : void 0,
|
|
80415
|
+
label: offer.label
|
|
80416
|
+
});
|
|
80417
|
+
let res;
|
|
80418
|
+
try {
|
|
80419
|
+
res = await deps.api(`/${offer.dropId}/claim`, {
|
|
80420
|
+
method: "POST",
|
|
80421
|
+
body: {
|
|
80422
|
+
deviceId: deps.deviceId(),
|
|
80423
|
+
hostId,
|
|
80424
|
+
// We run the transfer engine, so a LIVE drop is ours to take.
|
|
80425
|
+
caps: ["fs", "xfer"],
|
|
80426
|
+
destParent: destDir,
|
|
80427
|
+
destName: sanitizeDropName(singleItem ? basename11(offer.label) : offer.label, "Files")
|
|
80428
|
+
}
|
|
80429
|
+
});
|
|
80430
|
+
} catch (e) {
|
|
80431
|
+
log6(`drop ${offer.dropId}: claim failed:`, e instanceof Error ? e.message : String(e));
|
|
80432
|
+
return;
|
|
80433
|
+
}
|
|
80434
|
+
if (!res.won) {
|
|
80435
|
+
log6(`drop ${offer.dropId}: ${res.reason ?? "not available"}`);
|
|
80436
|
+
return;
|
|
80437
|
+
}
|
|
80438
|
+
if (res.transport === "blob" && deps.fetchBlobDrop) {
|
|
80439
|
+
try {
|
|
80440
|
+
await deps.fetchBlobDrop({
|
|
80441
|
+
dropId: offer.dropId,
|
|
80442
|
+
destDir,
|
|
80443
|
+
claim: res,
|
|
80444
|
+
sender: offer.kind === "peer" ? offer.from.email || offer.from.name : void 0
|
|
80445
|
+
});
|
|
80446
|
+
await deps.api(`/${offer.dropId}/complete`, { method: "POST", body: { ok: true } });
|
|
80447
|
+
} catch (e) {
|
|
80448
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
80449
|
+
log6(`drop ${offer.dropId}: download failed:`, message);
|
|
80450
|
+
await deps.api(`/${offer.dropId}/complete`, { method: "POST", body: { ok: false, error: message } }).catch(() => {
|
|
80451
|
+
});
|
|
80452
|
+
}
|
|
80453
|
+
}
|
|
80454
|
+
}
|
|
80455
|
+
async function handleOffer(offer) {
|
|
80456
|
+
if (seen.has(offer.dropId))
|
|
80457
|
+
return;
|
|
80458
|
+
seen.add(offer.dropId);
|
|
80459
|
+
if (seen.size > 500)
|
|
80460
|
+
seen.clear();
|
|
80461
|
+
if (offer.autoAccept) {
|
|
80462
|
+
await claim(offer);
|
|
80463
|
+
return;
|
|
80464
|
+
}
|
|
80465
|
+
if (!deps.canPrompt()) {
|
|
80466
|
+
log6(`drop ${offer.dropId}: headless, staying silent`);
|
|
80467
|
+
return;
|
|
80468
|
+
}
|
|
80469
|
+
const ac = new AbortController();
|
|
80470
|
+
pending2.set(offer.dropId, ac);
|
|
80471
|
+
const timer = setTimeout(() => ac.abort(), CONSENT_TIMEOUT_MS);
|
|
80472
|
+
let accepted = false;
|
|
80473
|
+
try {
|
|
80474
|
+
accepted = await deps.confirm({
|
|
80475
|
+
title: "Accept files?",
|
|
80476
|
+
// Sender identity is the SERVER-RESOLVED one. Rendering a sender-supplied
|
|
80477
|
+
// display name here would make "IT Department ✓" a one-line phishing kit.
|
|
80478
|
+
// No thumbnails and no raw filenames: the offer rings every device the user
|
|
80479
|
+
// owns, so the preview is the harassment payload.
|
|
80480
|
+
body: `${offer.from.email || offer.from.name} wants to send you ${offer.totalFiles === 1 ? "1 file" : `${offer.totalFiles} files`} (${formatBytes(offer.totalBytes)}).`
|
|
80481
|
+
}, ac.signal);
|
|
80482
|
+
} finally {
|
|
80483
|
+
clearTimeout(timer);
|
|
80484
|
+
pending2.delete(offer.dropId);
|
|
80485
|
+
}
|
|
80486
|
+
if (ac.signal.aborted) {
|
|
80487
|
+
log6(`drop ${offer.dropId}: prompt withdrawn`);
|
|
80488
|
+
return;
|
|
80489
|
+
}
|
|
80490
|
+
if (!accepted) {
|
|
80491
|
+
await deps.api(`/${offer.dropId}/decline`, { method: "POST" }).catch(() => {
|
|
80492
|
+
});
|
|
80493
|
+
return;
|
|
80494
|
+
}
|
|
80495
|
+
await claim(offer);
|
|
80496
|
+
}
|
|
80497
|
+
return {
|
|
80498
|
+
onFrame(msg) {
|
|
80499
|
+
switch (msg.type) {
|
|
80500
|
+
case "drop:offer":
|
|
80501
|
+
void handleOffer(msg).catch((e) => log6("offer failed:", e instanceof Error ? e.message : String(e)));
|
|
80502
|
+
return;
|
|
80503
|
+
case "drop:dismiss":
|
|
80504
|
+
pending2.get(msg.dropId)?.abort();
|
|
80505
|
+
pending2.delete(msg.dropId);
|
|
80506
|
+
seen.add(msg.dropId);
|
|
80507
|
+
return;
|
|
80508
|
+
case "drop:claimed":
|
|
80509
|
+
if (msg.senderHostId && msg.senderHostId === deps.ownHostId())
|
|
80510
|
+
deps.startLiveSend?.(msg);
|
|
80511
|
+
return;
|
|
80512
|
+
default:
|
|
80513
|
+
return;
|
|
80514
|
+
}
|
|
80515
|
+
},
|
|
80516
|
+
/** Catches offers that arrived while this device was offline. */
|
|
80517
|
+
async reconcile() {
|
|
80518
|
+
try {
|
|
80519
|
+
const rows = await deps.api("/inbox");
|
|
80520
|
+
for (const r of rows) {
|
|
80521
|
+
if (r.status && r.status !== "PENDING")
|
|
80522
|
+
continue;
|
|
80523
|
+
void handleOffer({ ...r, type: "drop:offer" });
|
|
80524
|
+
}
|
|
80525
|
+
} catch (e) {
|
|
80526
|
+
log6("inbox reconcile failed:", e instanceof Error ? e.message : String(e));
|
|
80527
|
+
}
|
|
80528
|
+
}
|
|
80529
|
+
};
|
|
80530
|
+
}
|
|
80531
|
+
|
|
80532
|
+
// ../../packages/host-core/dist/drops/blob-fetch.js
|
|
80533
|
+
import { createWriteStream as createWriteStream2 } from "node:fs";
|
|
80534
|
+
import { mkdir as mkdir12, rm as rm4, chmod as chmod3 } from "node:fs/promises";
|
|
80535
|
+
import { dirname as dirname22, join as join47 } from "node:path";
|
|
80536
|
+
import { Readable as Readable2 } from "node:stream";
|
|
80537
|
+
import { pipeline } from "node:stream/promises";
|
|
80538
|
+
|
|
80539
|
+
// ../../packages/host-core/dist/drops/crypto.js
|
|
80540
|
+
import { Transform } from "node:stream";
|
|
80541
|
+
var MAGIC = Buffer.from("TSD1", "ascii");
|
|
80542
|
+
var NONCE_BYTES = 12;
|
|
80543
|
+
var HEADER_BYTES = MAGIC.length + NONCE_BYTES;
|
|
80544
|
+
var FRAME_BYTES = 1024 * 1024;
|
|
80545
|
+
var TAG_BYTES = 16;
|
|
80546
|
+
var MAX_FRAME_CIPHERTEXT = FRAME_BYTES + TAG_BYTES + 1024;
|
|
80547
|
+
var subtle3 = () => globalThis.crypto.subtle;
|
|
80548
|
+
var bs3 = (b) => b;
|
|
80549
|
+
async function importKey(raw) {
|
|
80550
|
+
return subtle3().importKey("raw", bs3(raw), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
|
|
80551
|
+
}
|
|
80552
|
+
function nonceFor(base, index) {
|
|
80553
|
+
const n = Buffer.from(base);
|
|
80554
|
+
n[8] ^= index >>> 24 & 255;
|
|
80555
|
+
n[9] ^= index >>> 16 & 255;
|
|
80556
|
+
n[10] ^= index >>> 8 & 255;
|
|
80557
|
+
n[11] ^= index & 255;
|
|
80558
|
+
return n;
|
|
80559
|
+
}
|
|
80560
|
+
function aadFor(index) {
|
|
80561
|
+
const a = Buffer.alloc(4);
|
|
80562
|
+
a.writeUInt32BE(index >>> 0, 0);
|
|
80563
|
+
return a;
|
|
80564
|
+
}
|
|
80565
|
+
var ByteQueue = class {
|
|
80566
|
+
chunks = [];
|
|
80567
|
+
length = 0;
|
|
80568
|
+
push(b) {
|
|
80569
|
+
if (b.length) {
|
|
80570
|
+
this.chunks.push(b);
|
|
80571
|
+
this.length += b.length;
|
|
80572
|
+
}
|
|
80573
|
+
}
|
|
80574
|
+
/** The first `n` bytes, or null when we don't have them yet. Does not consume. */
|
|
80575
|
+
peek(n) {
|
|
80576
|
+
if (this.length < n)
|
|
80577
|
+
return null;
|
|
80578
|
+
const first = this.chunks[0];
|
|
80579
|
+
if (first.length >= n)
|
|
80580
|
+
return first.subarray(0, n);
|
|
80581
|
+
const out = Buffer.allocUnsafe(n);
|
|
80582
|
+
let off = 0;
|
|
80583
|
+
for (const c of this.chunks) {
|
|
80584
|
+
const take = Math.min(c.length, n - off);
|
|
80585
|
+
c.copy(out, off, 0, take);
|
|
80586
|
+
off += take;
|
|
80587
|
+
if (off === n)
|
|
80588
|
+
break;
|
|
80589
|
+
}
|
|
80590
|
+
return out;
|
|
80591
|
+
}
|
|
80592
|
+
consume(n) {
|
|
80593
|
+
let left = n;
|
|
80594
|
+
while (left > 0) {
|
|
80595
|
+
const c = this.chunks[0];
|
|
80596
|
+
if (c.length <= left) {
|
|
80597
|
+
left -= c.length;
|
|
80598
|
+
this.length -= c.length;
|
|
80599
|
+
this.chunks.shift();
|
|
80600
|
+
} else {
|
|
80601
|
+
this.chunks[0] = c.subarray(left);
|
|
80602
|
+
this.length -= left;
|
|
80603
|
+
left = 0;
|
|
80604
|
+
}
|
|
80605
|
+
}
|
|
80606
|
+
}
|
|
80607
|
+
};
|
|
80608
|
+
function decryptStream(source, keyRaw) {
|
|
80609
|
+
let key2 = null;
|
|
80610
|
+
let index = 0;
|
|
80611
|
+
let base = null;
|
|
80612
|
+
const q = new ByteQueue();
|
|
80613
|
+
let sawEnd = false;
|
|
80614
|
+
const t = new Transform({
|
|
80615
|
+
async transform(chunk, _enc, cb) {
|
|
80616
|
+
try {
|
|
80617
|
+
q.push(chunk);
|
|
80618
|
+
if (!base) {
|
|
80619
|
+
const head = q.peek(HEADER_BYTES);
|
|
80620
|
+
if (!head) {
|
|
80621
|
+
cb();
|
|
80622
|
+
return;
|
|
80623
|
+
}
|
|
80624
|
+
if (!head.subarray(0, MAGIC.length).equals(MAGIC))
|
|
80625
|
+
throw new Error("not an encrypted drop");
|
|
80626
|
+
base = Buffer.from(head.subarray(MAGIC.length, HEADER_BYTES));
|
|
80627
|
+
q.consume(HEADER_BYTES);
|
|
80628
|
+
key2 = await importKey(keyRaw);
|
|
80629
|
+
}
|
|
80630
|
+
while (!sawEnd) {
|
|
80631
|
+
const lenBuf = q.peek(4);
|
|
80632
|
+
if (!lenBuf)
|
|
80633
|
+
break;
|
|
80634
|
+
const len = lenBuf.readUInt32BE(0);
|
|
80635
|
+
if (len === 0) {
|
|
80636
|
+
sawEnd = true;
|
|
80637
|
+
q.consume(4);
|
|
80638
|
+
break;
|
|
80639
|
+
}
|
|
80640
|
+
if (len > MAX_FRAME_CIPHERTEXT)
|
|
80641
|
+
throw new Error("encrypted frame is implausibly large");
|
|
80642
|
+
const frame = q.peek(4 + len);
|
|
80643
|
+
if (!frame)
|
|
80644
|
+
break;
|
|
80645
|
+
const plain = Buffer.from(await subtle3().decrypt({ name: "AES-GCM", iv: bs3(nonceFor(base, index)), additionalData: bs3(aadFor(index)) }, key2, bs3(frame.subarray(4))));
|
|
80646
|
+
this.push(plain);
|
|
80647
|
+
index += 1;
|
|
80648
|
+
q.consume(4 + len);
|
|
80649
|
+
}
|
|
80650
|
+
cb();
|
|
80651
|
+
} catch (e) {
|
|
80652
|
+
cb(e);
|
|
80653
|
+
}
|
|
80654
|
+
},
|
|
80655
|
+
flush(cb) {
|
|
80656
|
+
cb(sawEnd ? null : new Error("the transfer ended early (incomplete or tampered)"));
|
|
80657
|
+
}
|
|
80658
|
+
});
|
|
80659
|
+
source.on("error", (e) => t.destroy(e));
|
|
80660
|
+
source.pipe(t);
|
|
80661
|
+
return t;
|
|
80662
|
+
}
|
|
80663
|
+
|
|
80664
|
+
// ../../packages/host-core/dist/drops/quarantine.js
|
|
80665
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
80666
|
+
import { writeFile as writeFile10 } from "node:fs/promises";
|
|
80667
|
+
import { promisify as promisify4 } from "node:util";
|
|
80668
|
+
var exec = promisify4(execFile5);
|
|
80669
|
+
async function markQuarantined(path6, sourceLabel) {
|
|
80670
|
+
try {
|
|
80671
|
+
if (process.platform === "darwin") {
|
|
80672
|
+
const stamp2 = Math.floor(Date.now() / 1e3).toString(16);
|
|
80673
|
+
const agent = sourceLabel.replace(/[^\w.@-]/g, "_").slice(0, 64) || "Tesseract";
|
|
80674
|
+
await exec("xattr", ["-w", "com.apple.quarantine", `0081;${stamp2};Tesseract;${agent}`, path6]);
|
|
80675
|
+
return;
|
|
80676
|
+
}
|
|
80677
|
+
if (process.platform === "win32") {
|
|
80678
|
+
await writeFile10(`${path6}:Zone.Identifier`, "[ZoneTransfer]\r\nZoneId=3\r\n", "utf-8");
|
|
80679
|
+
}
|
|
80680
|
+
} catch {
|
|
80681
|
+
}
|
|
80682
|
+
}
|
|
80683
|
+
|
|
80684
|
+
// ../../packages/host-core/dist/drops/blob-fetch.js
|
|
80685
|
+
var log7 = (...a) => console.log("[drops]", ...a);
|
|
80686
|
+
async function fetchBlobDrop(opts) {
|
|
80687
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
80688
|
+
const written = [];
|
|
80689
|
+
let contentKey;
|
|
80690
|
+
if (opts.sealedKey) {
|
|
80691
|
+
if (!opts.sealingPrivate)
|
|
80692
|
+
throw new Error("This device has no sealing key, so it cannot open the transfer.");
|
|
80693
|
+
const open5 = opts.openSeal ?? openFromPrivateKey;
|
|
80694
|
+
contentKey = await open5(await opts.sealingPrivate(), opts.sealedKey, new TextEncoder().encode(opts.dropId));
|
|
80695
|
+
}
|
|
80696
|
+
const totalBytes = opts.items.reduce((n, i) => n + (i.kind === "file" ? i.size : 0), 0);
|
|
80697
|
+
let bytesDone = 0;
|
|
80698
|
+
try {
|
|
80699
|
+
for (const item of opts.items) {
|
|
80700
|
+
if (item.kind !== "dir")
|
|
80701
|
+
continue;
|
|
80702
|
+
const rel = sanitizeDropRel(item.rel);
|
|
80703
|
+
if (!rel) {
|
|
80704
|
+
log7(`drop ${opts.dropId}: refusing directory "${item.rel}"`);
|
|
80705
|
+
continue;
|
|
80706
|
+
}
|
|
80707
|
+
const abs = join47(opts.destDir, rel);
|
|
80708
|
+
if (!isInsideRoot(abs, opts.destDir)) {
|
|
80709
|
+
log7(`drop ${opts.dropId}: refusing directory outside the destination`);
|
|
80710
|
+
continue;
|
|
80711
|
+
}
|
|
80712
|
+
await mkdir12(abs, { recursive: true });
|
|
80713
|
+
}
|
|
80714
|
+
for (const item of opts.items) {
|
|
80715
|
+
if (opts.signal?.aborted)
|
|
80716
|
+
throw new Error("Cancelled.");
|
|
80717
|
+
if (item.kind !== "file")
|
|
80718
|
+
continue;
|
|
80719
|
+
if (!item.url) {
|
|
80720
|
+
log7(`drop ${opts.dropId}: item ${item.itemId} has no bytes, skipping`);
|
|
80721
|
+
continue;
|
|
80722
|
+
}
|
|
80723
|
+
const rel = sanitizeDropRel(item.rel);
|
|
80724
|
+
if (!rel) {
|
|
80725
|
+
log7(`drop ${opts.dropId}: refusing "${item.rel}"`);
|
|
80726
|
+
continue;
|
|
80727
|
+
}
|
|
80728
|
+
let abs = join47(opts.destDir, rel);
|
|
80729
|
+
if (!isInsideRoot(abs, opts.destDir)) {
|
|
80730
|
+
log7(`drop ${opts.dropId}: refusing a path outside the destination`);
|
|
80731
|
+
continue;
|
|
80732
|
+
}
|
|
80733
|
+
await mkdir12(dirname22(abs), { recursive: true });
|
|
80734
|
+
abs = await uniqueDropPath(abs);
|
|
80735
|
+
const res = await fetchFn(new URL(item.url, opts.apiOrigin).toString(), { signal: opts.signal });
|
|
80736
|
+
if (!res.ok || !res.body)
|
|
80737
|
+
throw new Error(`Download failed (${res.status}).`);
|
|
80738
|
+
const body = Readable2.fromWeb(res.body);
|
|
80739
|
+
const plain = contentKey ? decryptStream(body, contentKey) : body;
|
|
80740
|
+
try {
|
|
80741
|
+
await pipeline(plain, createWriteStream2(abs));
|
|
80742
|
+
} catch (e) {
|
|
80743
|
+
await rm4(abs, { force: true }).catch(() => {
|
|
80744
|
+
});
|
|
80745
|
+
throw e;
|
|
80746
|
+
}
|
|
80747
|
+
await chmod3(abs, 420).catch(() => {
|
|
80748
|
+
});
|
|
80749
|
+
if (opts.sender)
|
|
80750
|
+
await markQuarantined(abs, opts.sender);
|
|
80751
|
+
written.push(abs);
|
|
80752
|
+
bytesDone += item.size;
|
|
80753
|
+
opts.onProgress?.(bytesDone, totalBytes);
|
|
80754
|
+
}
|
|
80755
|
+
} catch (e) {
|
|
80756
|
+
for (const p of written)
|
|
80757
|
+
await rm4(p, { force: true }).catch(() => {
|
|
80758
|
+
});
|
|
80759
|
+
throw e;
|
|
80760
|
+
}
|
|
80761
|
+
return { written };
|
|
80762
|
+
}
|
|
80763
|
+
|
|
79572
80764
|
// ../../packages/host-core/dist/chat-agent.js
|
|
79573
|
-
import { randomUUID as
|
|
80765
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
79574
80766
|
var TURN_TIMEOUT_MS = 4 * 6e4;
|
|
79575
80767
|
var PARTIAL_INTERVAL_MS = 700;
|
|
79576
80768
|
function createChatAgentRunner(deps) {
|
|
79577
|
-
const
|
|
80769
|
+
const log9 = deps.log ?? (() => {
|
|
79578
80770
|
});
|
|
79579
80771
|
const active = /* @__PURE__ */ new Set();
|
|
79580
80772
|
async function api(path6, init) {
|
|
@@ -79598,7 +80790,7 @@ function createChatAgentRunner(deps) {
|
|
|
79598
80790
|
}
|
|
79599
80791
|
async function runTurn(prompt, onPartial) {
|
|
79600
80792
|
const backend2 = await getSharedBackend();
|
|
79601
|
-
const clientRequestId = `chat-agent-${
|
|
80793
|
+
const clientRequestId = `chat-agent-${randomUUID17()}`;
|
|
79602
80794
|
return new Promise((resolve12, reject) => {
|
|
79603
80795
|
let text8 = "";
|
|
79604
80796
|
let settled = false;
|
|
@@ -79662,7 +80854,7 @@ function createChatAgentRunner(deps) {
|
|
|
79662
80854
|
body: { deviceId: deps.deviceId }
|
|
79663
80855
|
});
|
|
79664
80856
|
if (!claimed) {
|
|
79665
|
-
|
|
80857
|
+
log9("chat-agent: job already claimed elsewhere", event.jobId);
|
|
79666
80858
|
return;
|
|
79667
80859
|
}
|
|
79668
80860
|
const ctx2 = await api(`/api/core/tesseract/chat/agent-jobs/${event.jobId}/context`);
|
|
@@ -79686,10 +80878,10 @@ function createChatAgentRunner(deps) {
|
|
|
79686
80878
|
await api(`/api/core/tesseract/chat/agent-jobs/${event.jobId}/reply`, { method: "POST", body: { markdown: trimmed } });
|
|
79687
80879
|
}
|
|
79688
80880
|
await api(`/api/core/tesseract/chat/agent-jobs/${event.jobId}/complete`, { method: "POST", body: {} });
|
|
79689
|
-
|
|
80881
|
+
log9("chat-agent: answered", event.jobId);
|
|
79690
80882
|
} catch (err2) {
|
|
79691
80883
|
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
79692
|
-
|
|
80884
|
+
log9("chat-agent: job failed", event.jobId, message);
|
|
79693
80885
|
await api(`/api/core/tesseract/chat/agent-jobs/${event.jobId}/complete`, {
|
|
79694
80886
|
method: "POST",
|
|
79695
80887
|
body: { error: message }
|
|
@@ -79879,9 +81071,9 @@ function registerRdpEngine() {
|
|
|
79879
81071
|
|
|
79880
81072
|
// ../../packages/host-core/dist/rdesk/audit.js
|
|
79881
81073
|
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
79882
|
-
import { join as
|
|
81074
|
+
import { join as join48 } from "node:path";
|
|
79883
81075
|
function rdeskAuditPath() {
|
|
79884
|
-
return
|
|
81076
|
+
return join48(daemonDir2(), "rdesk-audit.jsonl");
|
|
79885
81077
|
}
|
|
79886
81078
|
function appendRdeskAudit(e) {
|
|
79887
81079
|
try {
|
|
@@ -79892,10 +81084,10 @@ function appendRdeskAudit(e) {
|
|
|
79892
81084
|
|
|
79893
81085
|
// ../../packages/host-core/dist/rdesk/password.js
|
|
79894
81086
|
import { scryptSync, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
|
|
79895
|
-
import { readFileSync as readFileSync29, writeFileSync as writeFileSync21, existsSync as existsSync24, unlinkSync as
|
|
79896
|
-
import { join as
|
|
81087
|
+
import { readFileSync as readFileSync29, writeFileSync as writeFileSync21, existsSync as existsSync24, unlinkSync as unlinkSync3 } from "node:fs";
|
|
81088
|
+
import { join as join49 } from "node:path";
|
|
79897
81089
|
function pwFile() {
|
|
79898
|
-
return
|
|
81090
|
+
return join49(daemonDir2(), "rdesk-password.json");
|
|
79899
81091
|
}
|
|
79900
81092
|
function verifyRdeskPassword(password) {
|
|
79901
81093
|
if (!password)
|
|
@@ -79992,26 +81184,26 @@ function broadcastFileProviderHostsChanged() {
|
|
|
79992
81184
|
}
|
|
79993
81185
|
var BRIDGE_PORTS = [47821, 47822, 47823, 47824, 47825, 47826, 47827, 47828];
|
|
79994
81186
|
var BRIDGE_TOKEN = "b7c1f9a4e25d4c3a8f06e1d72b9a3c5e";
|
|
79995
|
-
function startFileProviderBridge(
|
|
81187
|
+
function startFileProviderBridge(log9 = () => {
|
|
79996
81188
|
}) {
|
|
79997
81189
|
if (process.platform !== "darwin" || wss)
|
|
79998
81190
|
return;
|
|
79999
81191
|
stopping = false;
|
|
80000
81192
|
token = BRIDGE_TOKEN;
|
|
80001
|
-
bindBridge(0,
|
|
81193
|
+
bindBridge(0, log9);
|
|
80002
81194
|
}
|
|
80003
|
-
function bindBridge(i,
|
|
81195
|
+
function bindBridge(i, log9) {
|
|
80004
81196
|
if (stopping || wss)
|
|
80005
81197
|
return;
|
|
80006
81198
|
if (i >= BRIDGE_PORTS.length) {
|
|
80007
|
-
|
|
81199
|
+
log9("fileprovider: no free bridge port in the fixed set");
|
|
80008
81200
|
return;
|
|
80009
81201
|
}
|
|
80010
81202
|
const port = BRIDGE_PORTS[i];
|
|
80011
81203
|
const server = new import_websocket_server.default({ host: "127.0.0.1", port });
|
|
80012
81204
|
server.on("listening", () => {
|
|
80013
81205
|
wss = server;
|
|
80014
|
-
|
|
81206
|
+
log9(`fileprovider bridge listening on 127.0.0.1:${port}`);
|
|
80015
81207
|
});
|
|
80016
81208
|
server.on("connection", (ws, req) => {
|
|
80017
81209
|
let ok = false;
|
|
@@ -80037,9 +81229,9 @@ function bindBridge(i, log5) {
|
|
|
80037
81229
|
server.close();
|
|
80038
81230
|
} catch {
|
|
80039
81231
|
}
|
|
80040
|
-
bindBridge(i + 1,
|
|
81232
|
+
bindBridge(i + 1, log9);
|
|
80041
81233
|
} else
|
|
80042
|
-
|
|
81234
|
+
log9("fileprovider bridge error", e?.message);
|
|
80043
81235
|
});
|
|
80044
81236
|
}
|
|
80045
81237
|
|
|
@@ -80047,12 +81239,12 @@ function bindBridge(i, log5) {
|
|
|
80047
81239
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
80048
81240
|
import { existsSync as existsSync25, mkdirSync as mkdirSync23, writeFileSync as writeFileSync22, rmSync as rmSync7 } from "node:fs";
|
|
80049
81241
|
import { homedir as homedir6 } from "node:os";
|
|
80050
|
-
import { join as
|
|
81242
|
+
import { join as join50 } from "node:path";
|
|
80051
81243
|
|
|
80052
81244
|
// ../../packages/host-core/dist/vault/peer-verify.js
|
|
80053
|
-
import { execFile as
|
|
80054
|
-
import { promisify as
|
|
80055
|
-
var run2 =
|
|
81245
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
81246
|
+
import { promisify as promisify5 } from "node:util";
|
|
81247
|
+
var run2 = promisify5(execFile6);
|
|
80056
81248
|
var APPEX_IDENTIFIER = "dev.tesseract.AutoFill";
|
|
80057
81249
|
async function teamIdOf(path6) {
|
|
80058
81250
|
try {
|
|
@@ -80109,13 +81301,13 @@ async function isTrustedAutofillPeer(remotePort) {
|
|
|
80109
81301
|
var APPEX_BUNDLE_ID = "dev.tesseract.AutoFill";
|
|
80110
81302
|
var BRIDGE_PORTS2 = [17872, 17873, 17874, 17875];
|
|
80111
81303
|
function appexContainerData() {
|
|
80112
|
-
return
|
|
81304
|
+
return join50(homedir6(), "Library", "Containers", APPEX_BUNDLE_ID, "Data");
|
|
80113
81305
|
}
|
|
80114
81306
|
function bridgeInfoPath() {
|
|
80115
|
-
return
|
|
81307
|
+
return join50(appexContainerData(), "Library", "Application Support", "tesseract-autofill-bridge.json");
|
|
80116
81308
|
}
|
|
80117
81309
|
var infoLoggedOnce = false;
|
|
80118
|
-
function writeBridgeInfo(port, token3,
|
|
81310
|
+
function writeBridgeInfo(port, token3, log9) {
|
|
80119
81311
|
if (process.platform !== "darwin")
|
|
80120
81312
|
return;
|
|
80121
81313
|
const containerData = appexContainerData();
|
|
@@ -80123,21 +81315,21 @@ function writeBridgeInfo(port, token3, log5) {
|
|
|
80123
81315
|
if (!existsSync25(containerData)) {
|
|
80124
81316
|
if (!infoLoggedOnce) {
|
|
80125
81317
|
infoLoggedOnce = true;
|
|
80126
|
-
|
|
81318
|
+
log9("autofill bridge-info SKIP: no appex container yet at", containerData);
|
|
80127
81319
|
}
|
|
80128
81320
|
return;
|
|
80129
81321
|
}
|
|
80130
|
-
const dir2 =
|
|
81322
|
+
const dir2 = join50(containerData, "Library", "Application Support");
|
|
80131
81323
|
mkdirSync23(dir2, { recursive: true });
|
|
80132
81324
|
writeFileSync22(bridgeInfoPath(), JSON.stringify({ port, token: token3, pid: process.pid, ts: Date.now() }), { mode: 384 });
|
|
80133
81325
|
if (!infoLoggedOnce) {
|
|
80134
81326
|
infoLoggedOnce = true;
|
|
80135
|
-
|
|
81327
|
+
log9("autofill bridge-info WROTE", port, "\u2192", dir2);
|
|
80136
81328
|
}
|
|
80137
81329
|
} catch (e) {
|
|
80138
81330
|
if (!infoLoggedOnce) {
|
|
80139
81331
|
infoLoggedOnce = true;
|
|
80140
|
-
|
|
81332
|
+
log9("autofill bridge-info FAILED:", e?.message);
|
|
80141
81333
|
}
|
|
80142
81334
|
}
|
|
80143
81335
|
}
|
|
@@ -80229,20 +81421,20 @@ var token2 = "";
|
|
|
80229
81421
|
var infoTimer = null;
|
|
80230
81422
|
var stopping2 = false;
|
|
80231
81423
|
var sockets2 = /* @__PURE__ */ new Set();
|
|
80232
|
-
function bind(i,
|
|
81424
|
+
function bind(i, log9) {
|
|
80233
81425
|
if (stopping2 || wss2)
|
|
80234
81426
|
return;
|
|
80235
81427
|
if (i >= BRIDGE_PORTS2.length) {
|
|
80236
|
-
|
|
81428
|
+
log9("autofill: no free bridge port in the fixed set");
|
|
80237
81429
|
return;
|
|
80238
81430
|
}
|
|
80239
81431
|
const port = BRIDGE_PORTS2[i];
|
|
80240
81432
|
const server = new import_websocket_server.default({ host: "127.0.0.1", port });
|
|
80241
81433
|
server.on("listening", () => {
|
|
80242
81434
|
wss2 = server;
|
|
80243
|
-
|
|
80244
|
-
writeBridgeInfo(port, token2,
|
|
80245
|
-
infoTimer = setInterval(() => writeBridgeInfo(port, token2,
|
|
81435
|
+
log9(`autofill bridge listening on 127.0.0.1:${port}`);
|
|
81436
|
+
writeBridgeInfo(port, token2, log9);
|
|
81437
|
+
infoTimer = setInterval(() => writeBridgeInfo(port, token2, log9), 15e3);
|
|
80246
81438
|
infoTimer.unref?.();
|
|
80247
81439
|
});
|
|
80248
81440
|
server.on("connection", (ws, req) => {
|
|
@@ -80270,18 +81462,18 @@ function bind(i, log5) {
|
|
|
80270
81462
|
server.close();
|
|
80271
81463
|
} catch {
|
|
80272
81464
|
}
|
|
80273
|
-
bind(i + 1,
|
|
81465
|
+
bind(i + 1, log9);
|
|
80274
81466
|
} else
|
|
80275
|
-
|
|
81467
|
+
log9("autofill bridge error", e?.message);
|
|
80276
81468
|
});
|
|
80277
81469
|
}
|
|
80278
|
-
function startAutofillBridge(
|
|
81470
|
+
function startAutofillBridge(log9 = () => {
|
|
80279
81471
|
}) {
|
|
80280
81472
|
if (process.platform !== "darwin" || wss2)
|
|
80281
81473
|
return;
|
|
80282
81474
|
stopping2 = false;
|
|
80283
81475
|
token2 = randomBytes7(24).toString("base64url");
|
|
80284
|
-
bind(0,
|
|
81476
|
+
bind(0, log9);
|
|
80285
81477
|
}
|
|
80286
81478
|
|
|
80287
81479
|
// ../../packages/host-core/dist/power-node.js
|
|
@@ -80345,7 +81537,7 @@ function createNodePowerController() {
|
|
|
80345
81537
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
80346
81538
|
import { chmodSync as chmodSync4, existsSync as existsSync26, mkdirSync as mkdirSync24, writeFileSync as writeFileSync23 } from "node:fs";
|
|
80347
81539
|
import { homedir as homedir7, platform as platform2 } from "node:os";
|
|
80348
|
-
import { dirname as
|
|
81540
|
+
import { dirname as dirname23, join as join51, resolve as resolve10 } from "node:path";
|
|
80349
81541
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
80350
81542
|
var HOST_NAME = "com.tesseract.companion";
|
|
80351
81543
|
var EXTENSION_IDS = ["icpnghhpcmaonjckkpilpajblfehpplo", "giolfpookdhjfafcchaflhmigghaeffn"];
|
|
@@ -80354,21 +81546,21 @@ function browserProfileDirs() {
|
|
|
80354
81546
|
const home = homedir7();
|
|
80355
81547
|
const p = platform2();
|
|
80356
81548
|
if (p === "darwin") {
|
|
80357
|
-
const base =
|
|
80358
|
-
return ["Google/Chrome", "Google/Chrome Beta", "Google/Chrome Dev", "Google/Chrome Canary", "Chromium", "Microsoft Edge", "BraveSoftware/Brave-Browser", "Vivaldi", "Arc"].map((b) =>
|
|
81549
|
+
const base = join51(home, "Library/Application Support");
|
|
81550
|
+
return ["Google/Chrome", "Google/Chrome Beta", "Google/Chrome Dev", "Google/Chrome Canary", "Chromium", "Microsoft Edge", "BraveSoftware/Brave-Browser", "Vivaldi", "Arc"].map((b) => join51(base, b));
|
|
80359
81551
|
}
|
|
80360
81552
|
if (p === "win32")
|
|
80361
81553
|
return [];
|
|
80362
|
-
const cfg2 =
|
|
80363
|
-
return ["google-chrome", "google-chrome-beta", "chromium", "microsoft-edge", "BraveSoftware/Brave-Browser", "vivaldi"].map((b) =>
|
|
81554
|
+
const cfg2 = join51(home, ".config");
|
|
81555
|
+
return ["google-chrome", "google-chrome-beta", "chromium", "microsoft-edge", "BraveSoftware/Brave-Browser", "vivaldi"].map((b) => join51(cfg2, b));
|
|
80364
81556
|
}
|
|
80365
|
-
function resolveBridge(
|
|
81557
|
+
function resolveBridge(log9) {
|
|
80366
81558
|
const candidates = [
|
|
80367
81559
|
process.env.CCB_COMPANION_HOST_JS,
|
|
80368
|
-
process.resourcesPath ?
|
|
81560
|
+
process.resourcesPath ? join51(process.resourcesPath, "companion-host", BRIDGE_BASENAME) : void 0
|
|
80369
81561
|
];
|
|
80370
81562
|
try {
|
|
80371
|
-
const here =
|
|
81563
|
+
const here = dirname23(fileURLToPath3(import.meta.url));
|
|
80372
81564
|
candidates.push(resolve10(here, "../../../apps/companion-host/dist", BRIDGE_BASENAME));
|
|
80373
81565
|
candidates.push(resolve10(here, "../../apps/companion-host/dist", BRIDGE_BASENAME));
|
|
80374
81566
|
} catch {
|
|
@@ -80376,12 +81568,12 @@ function resolveBridge(log5) {
|
|
|
80376
81568
|
for (const c of candidates)
|
|
80377
81569
|
if (c && existsSync26(c))
|
|
80378
81570
|
return c;
|
|
80379
|
-
|
|
81571
|
+
log9("companion native-host: bridge not found; skipping registration (looked in", candidates.filter(Boolean).join(" , "), ")");
|
|
80380
81572
|
return null;
|
|
80381
81573
|
}
|
|
80382
81574
|
function writeLauncher(daemonDir3, bridge, runtime) {
|
|
80383
81575
|
const isWin = platform2() === "win32";
|
|
80384
|
-
const launcher =
|
|
81576
|
+
const launcher = join51(daemonDir3, isWin ? "companion-host.cmd" : "companion-host.sh");
|
|
80385
81577
|
if (isWin) {
|
|
80386
81578
|
writeFileSync23(launcher, `@echo off\r
|
|
80387
81579
|
set ELECTRON_RUN_AS_NODE=1\r
|
|
@@ -80397,9 +81589,9 @@ ELECTRON_RUN_AS_NODE=1 exec "${runtime}" "${bridge}" "$@"
|
|
|
80397
81589
|
return launcher;
|
|
80398
81590
|
}
|
|
80399
81591
|
function registerNativeHost(opts) {
|
|
80400
|
-
const
|
|
81592
|
+
const log9 = opts.log ?? (() => void 0);
|
|
80401
81593
|
try {
|
|
80402
|
-
const bridge = resolveBridge(
|
|
81594
|
+
const bridge = resolveBridge(log9);
|
|
80403
81595
|
if (!bridge)
|
|
80404
81596
|
return;
|
|
80405
81597
|
const runtime = opts.runtime ?? process.execPath;
|
|
@@ -80413,27 +81605,27 @@ function registerNativeHost(opts) {
|
|
|
80413
81605
|
allowed_origins: EXTENSION_IDS.map((id) => `chrome-extension://${id}/`)
|
|
80414
81606
|
}, null, 2);
|
|
80415
81607
|
if (platform2() === "win32")
|
|
80416
|
-
return registerWindows(opts.daemonDir, manifestJson,
|
|
81608
|
+
return registerWindows(opts.daemonDir, manifestJson, log9);
|
|
80417
81609
|
let wrote = 0;
|
|
80418
81610
|
for (const profile of browserProfileDirs()) {
|
|
80419
81611
|
if (!existsSync26(profile))
|
|
80420
81612
|
continue;
|
|
80421
81613
|
try {
|
|
80422
|
-
const dir2 =
|
|
81614
|
+
const dir2 = join51(profile, "NativeMessagingHosts");
|
|
80423
81615
|
mkdirSync24(dir2, { recursive: true });
|
|
80424
|
-
writeFileSync23(
|
|
81616
|
+
writeFileSync23(join51(dir2, `${HOST_NAME}.json`), manifestJson);
|
|
80425
81617
|
wrote++;
|
|
80426
81618
|
} catch {
|
|
80427
81619
|
}
|
|
80428
81620
|
}
|
|
80429
|
-
|
|
81621
|
+
log9(`companion native-host: registered for ${wrote} browser(s) \u2192 ${launcher}`);
|
|
80430
81622
|
} catch (e) {
|
|
80431
|
-
|
|
81623
|
+
log9("companion native-host: registration failed", e.message);
|
|
80432
81624
|
}
|
|
80433
81625
|
}
|
|
80434
|
-
function registerWindows(daemonDir3, manifestJson,
|
|
81626
|
+
function registerWindows(daemonDir3, manifestJson, log9) {
|
|
80435
81627
|
try {
|
|
80436
|
-
const manifestPath =
|
|
81628
|
+
const manifestPath = join51(daemonDir3, `${HOST_NAME}.json`);
|
|
80437
81629
|
writeFileSync23(manifestPath, manifestJson);
|
|
80438
81630
|
for (const root of ["Software\\Google\\Chrome", "Software\\Microsoft\\Edge", "Software\\BraveSoftware\\Brave-Browser"]) {
|
|
80439
81631
|
try {
|
|
@@ -80441,17 +81633,17 @@ function registerWindows(daemonDir3, manifestJson, log5) {
|
|
|
80441
81633
|
} catch {
|
|
80442
81634
|
}
|
|
80443
81635
|
}
|
|
80444
|
-
|
|
81636
|
+
log9("companion native-host: registered (Windows registry)");
|
|
80445
81637
|
} catch (e) {
|
|
80446
|
-
|
|
81638
|
+
log9("companion native-host: Windows registration failed", e.message);
|
|
80447
81639
|
}
|
|
80448
81640
|
}
|
|
80449
81641
|
|
|
80450
81642
|
// ../../packages/host-core/dist/activity/activity-tracker.js
|
|
80451
81643
|
import { promises as fsp, writeFileSync as writeFileSync24, mkdirSync as mkdirSync25, existsSync as existsSync27 } from "node:fs";
|
|
80452
|
-
import { join as
|
|
81644
|
+
import { join as join52 } from "node:path";
|
|
80453
81645
|
function activityProfilePath() {
|
|
80454
|
-
return
|
|
81646
|
+
return join52(daemonDir2(), "activity-profile.json");
|
|
80455
81647
|
}
|
|
80456
81648
|
var MAX_APPS = 300;
|
|
80457
81649
|
var MAX_AGE_DAYS = 120;
|
|
@@ -80494,7 +81686,7 @@ function loadMacAx2() {
|
|
|
80494
81686
|
function createActivityTracker(opts = {}) {
|
|
80495
81687
|
const intervalMs = opts.intervalMs ?? 5e3;
|
|
80496
81688
|
const flushEvery = opts.flushEvery ?? 12;
|
|
80497
|
-
const
|
|
81689
|
+
const log9 = opts.log ?? (() => {
|
|
80498
81690
|
});
|
|
80499
81691
|
let timer = null;
|
|
80500
81692
|
let profile = null;
|
|
@@ -80512,10 +81704,10 @@ function createActivityTracker(opts = {}) {
|
|
|
80512
81704
|
try {
|
|
80513
81705
|
await fsp.mkdir(daemonDir2(), { recursive: true, mode: 448 });
|
|
80514
81706
|
await fsp.writeFile(activityProfilePath(), JSON.stringify(profile), { mode: 384 });
|
|
80515
|
-
|
|
81707
|
+
log9(`activity flushed (${Object.keys(profile.apps).length} apps tracked)`);
|
|
80516
81708
|
dirty = 0;
|
|
80517
81709
|
} catch (e) {
|
|
80518
|
-
|
|
81710
|
+
log9("activity flush failed", e instanceof Error ? e.message : String(e));
|
|
80519
81711
|
}
|
|
80520
81712
|
}
|
|
80521
81713
|
async function sample() {
|
|
@@ -80527,7 +81719,7 @@ function createActivityTracker(opts = {}) {
|
|
|
80527
81719
|
const key2 = front.bundleId || front.name;
|
|
80528
81720
|
await ensureLoaded();
|
|
80529
81721
|
if (profile && Object.keys(profile.apps).length && !existsSync27(activityProfilePath())) {
|
|
80530
|
-
|
|
81722
|
+
log9("activity profile cleared externally \u2014 resetting in-memory");
|
|
80531
81723
|
profile = { version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), apps: {} };
|
|
80532
81724
|
}
|
|
80533
81725
|
const p = profile;
|
|
@@ -80541,14 +81733,14 @@ function createActivityTracker(opts = {}) {
|
|
|
80541
81733
|
if (++dirty >= flushEvery)
|
|
80542
81734
|
await flush2();
|
|
80543
81735
|
} catch (e) {
|
|
80544
|
-
|
|
81736
|
+
log9("activity sample failed", e instanceof Error ? e.message : String(e));
|
|
80545
81737
|
}
|
|
80546
81738
|
}
|
|
80547
81739
|
return {
|
|
80548
81740
|
start() {
|
|
80549
81741
|
if (timer)
|
|
80550
81742
|
return;
|
|
80551
|
-
|
|
81743
|
+
log9("activity tracking ON (local-only)");
|
|
80552
81744
|
timer = setInterval(() => {
|
|
80553
81745
|
void sample();
|
|
80554
81746
|
}, intervalMs);
|
|
@@ -80567,9 +81759,9 @@ function createActivityTracker(opts = {}) {
|
|
|
80567
81759
|
dirty = 0;
|
|
80568
81760
|
}
|
|
80569
81761
|
} catch (e) {
|
|
80570
|
-
|
|
81762
|
+
log9("activity final flush failed", e instanceof Error ? e.message : String(e));
|
|
80571
81763
|
}
|
|
80572
|
-
|
|
81764
|
+
log9("activity tracking OFF");
|
|
80573
81765
|
},
|
|
80574
81766
|
running() {
|
|
80575
81767
|
return timer !== null;
|
|
@@ -80596,16 +81788,68 @@ function getRdeskClientWebContents() {
|
|
|
80596
81788
|
} };
|
|
80597
81789
|
}
|
|
80598
81790
|
|
|
81791
|
+
// ../../packages/host-core/dist/gui/rdesk-call-window.js
|
|
81792
|
+
import { join as join53 } from "node:path";
|
|
81793
|
+
var win2 = null;
|
|
81794
|
+
function showRdeskCallWindow(opts) {
|
|
81795
|
+
const el2 = electronModule();
|
|
81796
|
+
if (!el2?.BrowserWindow)
|
|
81797
|
+
return;
|
|
81798
|
+
if (win2 && !win2.isDestroyed()) {
|
|
81799
|
+
try {
|
|
81800
|
+
win2.focus();
|
|
81801
|
+
} catch {
|
|
81802
|
+
}
|
|
81803
|
+
return;
|
|
81804
|
+
}
|
|
81805
|
+
const w = new el2.BrowserWindow({
|
|
81806
|
+
width: 380,
|
|
81807
|
+
height: 320,
|
|
81808
|
+
resizable: true,
|
|
81809
|
+
minimizable: true,
|
|
81810
|
+
fullscreenable: false,
|
|
81811
|
+
alwaysOnTop: true,
|
|
81812
|
+
skipTaskbar: false,
|
|
81813
|
+
title: "Remote desktop \u2014 call",
|
|
81814
|
+
backgroundColor: "#15151a",
|
|
81815
|
+
webPreferences: { contextIsolation: true, sandbox: false }
|
|
81816
|
+
});
|
|
81817
|
+
try {
|
|
81818
|
+
w.setAlwaysOnTop(true, "floating");
|
|
81819
|
+
} catch {
|
|
81820
|
+
}
|
|
81821
|
+
w.on("closed", () => {
|
|
81822
|
+
win2 = null;
|
|
81823
|
+
try {
|
|
81824
|
+
opts.onClosed?.();
|
|
81825
|
+
} catch {
|
|
81826
|
+
}
|
|
81827
|
+
});
|
|
81828
|
+
const loadFile = w.loadFile;
|
|
81829
|
+
void loadFile(join53(daemonUiDir(), "rdesk-call.html"), { query: { url: opts.url, token: opts.token, room: opts.room } }).catch((e) => console.error("[rdesk-call] loadFile FAILED", e?.message ?? e));
|
|
81830
|
+
win2 = w;
|
|
81831
|
+
}
|
|
81832
|
+
function closeRdeskCallWindow() {
|
|
81833
|
+
const w = win2;
|
|
81834
|
+
win2 = null;
|
|
81835
|
+
if (w && !w.isDestroyed()) {
|
|
81836
|
+
try {
|
|
81837
|
+
w.close();
|
|
81838
|
+
} catch {
|
|
81839
|
+
}
|
|
81840
|
+
}
|
|
81841
|
+
}
|
|
81842
|
+
|
|
80599
81843
|
// ../../packages/host-core/dist/daemon-runtime.js
|
|
80600
81844
|
var daemonClipboardSync = null;
|
|
80601
81845
|
var REFRESH_INTERVAL_MS = 12 * 60 * 60 * 1e3;
|
|
80602
81846
|
async function runDaemon(opts = {}) {
|
|
80603
81847
|
const VERSION = opts.version ?? process.env.CCB_DAEMON_VERSION ?? "dev";
|
|
80604
81848
|
const TAG = opts.tag ?? "host-daemon";
|
|
80605
|
-
const
|
|
80606
|
-
|
|
80607
|
-
const pidfile = () =>
|
|
80608
|
-
const statusFile = () =>
|
|
81849
|
+
const log9 = (...args) => console.log(`[${TAG} ${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args);
|
|
81850
|
+
log9("paths", JSON.stringify(sealBasePaths()));
|
|
81851
|
+
const pidfile = () => join54(daemonDir2(), "daemon.lock");
|
|
81852
|
+
const statusFile = () => join54(daemonDir2(), "status.json");
|
|
80609
81853
|
const isAlive2 = (pid) => {
|
|
80610
81854
|
try {
|
|
80611
81855
|
process.kill(pid, 0);
|
|
@@ -80632,7 +81876,7 @@ async function runDaemon(opts = {}) {
|
|
|
80632
81876
|
return false;
|
|
80633
81877
|
if (prev !== process.pid && isAlive2(prev))
|
|
80634
81878
|
return false;
|
|
80635
|
-
|
|
81879
|
+
unlinkSync4(f);
|
|
80636
81880
|
} catch {
|
|
80637
81881
|
return false;
|
|
80638
81882
|
}
|
|
@@ -80644,7 +81888,7 @@ async function runDaemon(opts = {}) {
|
|
|
80644
81888
|
try {
|
|
80645
81889
|
const f = pidfile();
|
|
80646
81890
|
if (existsSync28(f) && Number(readFileSync30(f, "utf-8").trim()) === process.pid)
|
|
80647
|
-
|
|
81891
|
+
unlinkSync4(f);
|
|
80648
81892
|
} catch {
|
|
80649
81893
|
}
|
|
80650
81894
|
}
|
|
@@ -80704,7 +81948,7 @@ async function runDaemon(opts = {}) {
|
|
|
80704
81948
|
const live = [...rivals].filter(isAlive2);
|
|
80705
81949
|
if (live.length) {
|
|
80706
81950
|
for (const pid of live) {
|
|
80707
|
-
|
|
81951
|
+
log9(`takeover: killing rival daemon pid=${pid} (latest-wins single-instance)`);
|
|
80708
81952
|
try {
|
|
80709
81953
|
process.kill(pid, "SIGTERM");
|
|
80710
81954
|
} catch {
|
|
@@ -80714,7 +81958,7 @@ async function runDaemon(opts = {}) {
|
|
|
80714
81958
|
await new Promise((r) => setTimeout(r, 100));
|
|
80715
81959
|
for (const pid of live)
|
|
80716
81960
|
if (isAlive2(pid)) {
|
|
80717
|
-
|
|
81961
|
+
log9(`takeover: SIGKILL rival pid=${pid} (ignored SIGTERM)`);
|
|
80718
81962
|
try {
|
|
80719
81963
|
process.kill(pid, "SIGKILL");
|
|
80720
81964
|
} catch {
|
|
@@ -80723,7 +81967,7 @@ async function runDaemon(opts = {}) {
|
|
|
80723
81967
|
}
|
|
80724
81968
|
try {
|
|
80725
81969
|
if (existsSync28(pidfile()))
|
|
80726
|
-
|
|
81970
|
+
unlinkSync4(pidfile());
|
|
80727
81971
|
} catch {
|
|
80728
81972
|
}
|
|
80729
81973
|
}
|
|
@@ -80738,7 +81982,7 @@ async function runDaemon(opts = {}) {
|
|
|
80738
81982
|
await takeoverMachine();
|
|
80739
81983
|
if (!acquireLock2()) {
|
|
80740
81984
|
try {
|
|
80741
|
-
|
|
81985
|
+
unlinkSync4(pidfile());
|
|
80742
81986
|
} catch {
|
|
80743
81987
|
}
|
|
80744
81988
|
if (!acquireLock2()) {
|
|
@@ -80749,26 +81993,30 @@ async function runDaemon(opts = {}) {
|
|
|
80749
81993
|
}
|
|
80750
81994
|
}
|
|
80751
81995
|
}
|
|
81996
|
+
try {
|
|
81997
|
+
unlinkSync4(join54(daemonDir2(), "daemon.booting"));
|
|
81998
|
+
} catch {
|
|
81999
|
+
}
|
|
80752
82000
|
let timer = null;
|
|
80753
82001
|
let localServer = null;
|
|
80754
82002
|
let localInfo = null;
|
|
80755
82003
|
let rdeskManager = null;
|
|
80756
82004
|
const loopbackRdeskSend = /* @__PURE__ */ new Map();
|
|
80757
|
-
const activityTracker = createActivityTracker({ log:
|
|
82005
|
+
const activityTracker = createActivityTracker({ log: log9 });
|
|
80758
82006
|
let activityTimer = null;
|
|
80759
82007
|
const creds3 = readCredentials();
|
|
80760
82008
|
const deviceId2 = creds3?.deviceId;
|
|
80761
82009
|
const daemonStartedAt = Date.now();
|
|
80762
82010
|
process.on("exit", (code) => {
|
|
80763
82011
|
try {
|
|
80764
|
-
|
|
82012
|
+
log9(`PROCESS EXIT code=${code} uptimeMs=${Date.now() - daemonStartedAt}`);
|
|
80765
82013
|
} catch {
|
|
80766
82014
|
}
|
|
80767
82015
|
});
|
|
80768
82016
|
function shutdown(code, reason = "unknown") {
|
|
80769
82017
|
try {
|
|
80770
|
-
|
|
80771
|
-
|
|
82018
|
+
log9(`SHUTDOWN reason=${reason} code=${code} uptimeMs=${Date.now() - daemonStartedAt} ppid=${process.ppid} pid=${process.pid}`);
|
|
82019
|
+
log9(`SHUTDOWN caller: ${(new Error("shutdown").stack ?? "").split("\n").slice(2, 7).map((l) => l.trim()).join(" <- ")}`);
|
|
80772
82020
|
} catch {
|
|
80773
82021
|
}
|
|
80774
82022
|
if (timer) {
|
|
@@ -80799,18 +82047,60 @@ async function runDaemon(opts = {}) {
|
|
|
80799
82047
|
}
|
|
80800
82048
|
process.on("SIGTERM", () => shutdown(0, "signal:SIGTERM"));
|
|
80801
82049
|
process.on("SIGINT", () => shutdown(0, "signal:SIGINT"));
|
|
82050
|
+
process.on("uncaughtException", (e) => {
|
|
82051
|
+
try {
|
|
82052
|
+
log9(`UNCAUGHT (kept alive): ${e instanceof Error ? e.stack ?? e.message : String(e)}`);
|
|
82053
|
+
} catch {
|
|
82054
|
+
}
|
|
82055
|
+
});
|
|
82056
|
+
process.on("unhandledRejection", (r) => {
|
|
82057
|
+
try {
|
|
82058
|
+
log9(`UNHANDLED REJECTION (kept alive): ${r instanceof Error ? r.stack ?? r.message : String(r)}`);
|
|
82059
|
+
} catch {
|
|
82060
|
+
}
|
|
82061
|
+
});
|
|
82062
|
+
setInterval(() => {
|
|
82063
|
+
}, 1 << 30);
|
|
82064
|
+
try {
|
|
82065
|
+
const eapp = electronModule()?.app;
|
|
82066
|
+
if (eapp) {
|
|
82067
|
+
eapp.on("before-quit", (e) => {
|
|
82068
|
+
try {
|
|
82069
|
+
log9("APP before-quit \u2014 PREVENTED (daemon never self-quits via Electron)");
|
|
82070
|
+
} catch {
|
|
82071
|
+
}
|
|
82072
|
+
try {
|
|
82073
|
+
e?.preventDefault?.();
|
|
82074
|
+
} catch {
|
|
82075
|
+
}
|
|
82076
|
+
});
|
|
82077
|
+
eapp.on("render-process-gone", (_e, d) => {
|
|
82078
|
+
try {
|
|
82079
|
+
log9(`render-process-gone (kept alive): ${JSON.stringify(d)}`);
|
|
82080
|
+
} catch {
|
|
82081
|
+
}
|
|
82082
|
+
});
|
|
82083
|
+
eapp.on("child-process-gone", (_e, d) => {
|
|
82084
|
+
try {
|
|
82085
|
+
log9(`child-process-gone (kept alive): ${JSON.stringify(d)}`);
|
|
82086
|
+
} catch {
|
|
82087
|
+
}
|
|
82088
|
+
});
|
|
82089
|
+
}
|
|
82090
|
+
} catch {
|
|
82091
|
+
}
|
|
80802
82092
|
if (process.env.CCB_RDESK_ROLE !== "client")
|
|
80803
82093
|
process.env.CCB_HOST_DAEMON = "1";
|
|
80804
82094
|
const localToken = randomBytes8(24).toString("hex");
|
|
80805
82095
|
try {
|
|
80806
|
-
localServer = await startLocalServer({ hostRoot: resolveHostRoot(), token: localToken, log:
|
|
82096
|
+
localServer = await startLocalServer({ hostRoot: resolveHostRoot(), token: localToken, log: log9, getRdesk: () => rdeskManager, loopbackRdeskSend });
|
|
80807
82097
|
localInfo = { localPort: localServer.port, localToken };
|
|
80808
82098
|
} catch (e) {
|
|
80809
|
-
|
|
82099
|
+
log9("local listener failed to start", e.message);
|
|
80810
82100
|
}
|
|
80811
82101
|
writeStatus({ deviceId: deviceId2, connected: false });
|
|
80812
82102
|
try {
|
|
80813
|
-
registerNativeHost({ daemonDir: daemonDir2(), log:
|
|
82103
|
+
registerNativeHost({ daemonDir: daemonDir2(), log: log9 });
|
|
80814
82104
|
} catch {
|
|
80815
82105
|
}
|
|
80816
82106
|
const syncActivity = () => {
|
|
@@ -80829,12 +82119,12 @@ async function runDaemon(opts = {}) {
|
|
|
80829
82119
|
try {
|
|
80830
82120
|
await opts.onOwned?.();
|
|
80831
82121
|
} catch (e) {
|
|
80832
|
-
|
|
82122
|
+
log9("onOwned hook failed", e.message);
|
|
80833
82123
|
}
|
|
80834
82124
|
if (!creds3) {
|
|
80835
82125
|
timer = setInterval(() => writeStatus({ deviceId: deviceId2, connected: false, localOnly: true }), 6e4);
|
|
80836
82126
|
writeStatus({ deviceId: deviceId2, connected: false, localOnly: true });
|
|
80837
|
-
|
|
82127
|
+
log9(`started LOCAL-ONLY (not signed in) \u2014 local :${localInfo?.localPort ?? "off"}, version ${VERSION}`);
|
|
80838
82128
|
return;
|
|
80839
82129
|
}
|
|
80840
82130
|
const c = creds3;
|
|
@@ -80843,10 +82133,20 @@ async function runDaemon(opts = {}) {
|
|
|
80843
82133
|
void (async () => {
|
|
80844
82134
|
try {
|
|
80845
82135
|
const id = await loadOrCreateDeviceIdentity();
|
|
80846
|
-
const
|
|
80847
|
-
|
|
82136
|
+
const sealing = await loadOrCreateSealingKey();
|
|
82137
|
+
const sealingPubSig = await attestSealingKey(id.privateKey, sealing.publicRaw);
|
|
82138
|
+
const ok = await registerDeviceKey({
|
|
82139
|
+
apiOrigin: c.apiOrigin,
|
|
82140
|
+
accessToken,
|
|
82141
|
+
deviceId: c.deviceId,
|
|
82142
|
+
publicRaw: id.publicRaw,
|
|
82143
|
+
label: hostname4(),
|
|
82144
|
+
sealingPub: b64u(sealing.publicRaw),
|
|
82145
|
+
sealingPubSig
|
|
82146
|
+
});
|
|
82147
|
+
log9(ok ? "e2e identity + sealing key registered" : "e2e identity registration not accepted (will retry on restart)");
|
|
80848
82148
|
} catch (e) {
|
|
80849
|
-
|
|
82149
|
+
log9("e2e identity registration failed (non-fatal)", e.message);
|
|
80850
82150
|
}
|
|
80851
82151
|
})();
|
|
80852
82152
|
async function refresh() {
|
|
@@ -80857,11 +82157,11 @@ async function runDaemon(opts = {}) {
|
|
|
80857
82157
|
body: JSON.stringify({ dtid: c.dtid, refreshSecret: c.refreshSecret })
|
|
80858
82158
|
});
|
|
80859
82159
|
if (!r.ok) {
|
|
80860
|
-
|
|
82160
|
+
log9("token refresh rejected", r.status);
|
|
80861
82161
|
if (r.status === 401) {
|
|
80862
|
-
|
|
82162
|
+
log9("device token no longer valid \u2014 dropping to local-only on restart");
|
|
80863
82163
|
try {
|
|
80864
|
-
|
|
82164
|
+
unlinkSync4(join54(daemonDir2(), "credentials.json"));
|
|
80865
82165
|
} catch {
|
|
80866
82166
|
}
|
|
80867
82167
|
shutdown(0, "token-revoked-401");
|
|
@@ -80873,15 +82173,53 @@ async function runDaemon(opts = {}) {
|
|
|
80873
82173
|
accessToken = data.accessToken;
|
|
80874
82174
|
writeCredentials({ ...c, accessToken: data.accessToken, expiresAt: data.expiresAt ?? c.expiresAt, publicId: data.publicId ?? c.publicId });
|
|
80875
82175
|
refreshControlLink();
|
|
80876
|
-
|
|
82176
|
+
log9("token refreshed");
|
|
80877
82177
|
}
|
|
80878
82178
|
} catch (e) {
|
|
80879
|
-
|
|
82179
|
+
log9("token refresh failed (will retry)", e.message);
|
|
80880
82180
|
}
|
|
80881
82181
|
}
|
|
80882
82182
|
registerTesseractEngine({ getIceServers: () => fetchIceServers() });
|
|
80883
82183
|
registerVncEngine();
|
|
80884
82184
|
registerRdpEngine();
|
|
82185
|
+
initDropReceiveUi({ hostLabel: (hostId) => listHosts().find((h) => h.hostId === hostId)?.name });
|
|
82186
|
+
const dropsBase = `${c.apiOrigin.replace(/\/$/, "")}/api/core/tesseract/drops`;
|
|
82187
|
+
const drops = createDropHandler({
|
|
82188
|
+
api: async (path6, init) => {
|
|
82189
|
+
const method = init?.method ?? "GET";
|
|
82190
|
+
const hasBody = method !== "GET" && method !== "HEAD";
|
|
82191
|
+
const r = await fetch(`${dropsBase}${path6}`, {
|
|
82192
|
+
method,
|
|
82193
|
+
headers: { authorization: `Bearer ${accessToken}`, ...hasBody ? { "content-type": "application/json" } : {} },
|
|
82194
|
+
...hasBody ? { body: JSON.stringify(init?.body ?? {}) } : {},
|
|
82195
|
+
signal: AbortSignal.timeout(1e4)
|
|
82196
|
+
});
|
|
82197
|
+
if (!r.ok)
|
|
82198
|
+
throw new Error(`drops ${path6} \u2192 ${r.status}`);
|
|
82199
|
+
return await r.json().catch(() => ({}));
|
|
82200
|
+
},
|
|
82201
|
+
deviceId: () => c.deviceId,
|
|
82202
|
+
ownHostId: () => ownHost() || null,
|
|
82203
|
+
canPrompt: () => !!electronModule(),
|
|
82204
|
+
confirm: async (spec, signal) => (await confirmAction(spec, { signal })).confirmed,
|
|
82205
|
+
// Store-and-forward: we won the claim, so pull the bytes and (for cross-user
|
|
82206
|
+
// drops) open the one seal in the fan-out that was made for THIS device.
|
|
82207
|
+
fetchBlobDrop: async ({ dropId, destDir, claim, sender }) => {
|
|
82208
|
+
await fetchBlobDrop({
|
|
82209
|
+
dropId,
|
|
82210
|
+
destDir,
|
|
82211
|
+
apiOrigin: c.apiOrigin,
|
|
82212
|
+
items: claim.items ?? [],
|
|
82213
|
+
sealedKey: claim.sealedKey,
|
|
82214
|
+
sealingPrivate: async () => (await loadOrCreateSealingKey()).privateKey,
|
|
82215
|
+
// Present ⇒ cross-user ⇒ quarantine, so the OS treats the file as foreign.
|
|
82216
|
+
sender
|
|
82217
|
+
});
|
|
82218
|
+
}
|
|
82219
|
+
// No startLiveSend here: SENDING is driven by the app (a client tells the source
|
|
82220
|
+
// host to begin, exactly like the existing copy-to-machine flow). The daemon is
|
|
82221
|
+
// the receiving half, and the source host it addresses may well be this daemon.
|
|
82222
|
+
});
|
|
80885
82223
|
let rdeskRef = null;
|
|
80886
82224
|
const rdesk = new RdeskSessionManager({
|
|
80887
82225
|
createEngine: createRemoteDesktopEngine,
|
|
@@ -80912,8 +82250,64 @@ async function runDaemon(opts = {}) {
|
|
|
80912
82250
|
onSessionsChanged: (sessions3) => {
|
|
80913
82251
|
writeStatus({ deviceId: c.deviceId, connected: true, rdeskSessions: sessions3.length });
|
|
80914
82252
|
setRdeskTraySessions(sessions3.length, () => void rdeskRef?.disconnectAll("host"));
|
|
82253
|
+
if (sessions3.length === 0)
|
|
82254
|
+
closeRdeskCallWindow();
|
|
82255
|
+
},
|
|
82256
|
+
// A viewer joined/left the in-session voice/video call → open/close the operator call window,
|
|
82257
|
+
// joined to the SAME LiveKit room (= the rdesk sessionId) the viewer is in, so the person at the
|
|
82258
|
+
// host can talk. Reuses the team-chat/huddle conferencing; the daemon mints its own join token
|
|
82259
|
+
// with the device token (identity `${sub}#host`, distinct from the viewer's).
|
|
82260
|
+
onCall: ({ sessionId, on }) => {
|
|
82261
|
+
if (!on) {
|
|
82262
|
+
closeRdeskCallWindow();
|
|
82263
|
+
return;
|
|
82264
|
+
}
|
|
82265
|
+
void (async () => {
|
|
82266
|
+
try {
|
|
82267
|
+
const r = await fetch(`${c.apiOrigin.replace(/\/$/, "")}/api/core/tesseract/conferencing/token`, {
|
|
82268
|
+
method: "POST",
|
|
82269
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${accessToken}` },
|
|
82270
|
+
body: JSON.stringify({ room: sessionId, identity: "host", name: hostname4() }),
|
|
82271
|
+
signal: AbortSignal.timeout(1e4)
|
|
82272
|
+
});
|
|
82273
|
+
if (!r.ok) {
|
|
82274
|
+
log9(`[rdesk call] token mint ${r.status} (conferencing configured?)`);
|
|
82275
|
+
return;
|
|
82276
|
+
}
|
|
82277
|
+
const j = await r.json().catch(() => ({}));
|
|
82278
|
+
if (!j.token || !j.url) {
|
|
82279
|
+
log9("[rdesk call] token response missing token/url");
|
|
82280
|
+
return;
|
|
82281
|
+
}
|
|
82282
|
+
showRdeskCallWindow({ url: j.url, token: j.token, room: sessionId });
|
|
82283
|
+
} catch (e) {
|
|
82284
|
+
log9("[rdesk call] token mint failed", e instanceof Error ? e.message : String(e));
|
|
82285
|
+
}
|
|
82286
|
+
})();
|
|
82287
|
+
},
|
|
82288
|
+
// In-session chat from the viewer → surface it to the operator as a native notification.
|
|
82289
|
+
// On macOS the notification carries an inline REPLY box (hasReply) whose text is sent
|
|
82290
|
+
// straight back via sendChat — a full operator chat loop with no extra window. Off-mac (no
|
|
82291
|
+
// hasReply) it's receive-only for now; the daemon log always records it as a fallback.
|
|
82292
|
+
onChat: ({ sessionId, viewerName, text: text8 }) => {
|
|
82293
|
+
const NotifCtor = electronModule()?.Notification;
|
|
82294
|
+
if (!NotifCtor) {
|
|
82295
|
+
log9(`[rdesk chat] ${viewerName ?? "viewer"}: ${text8}`);
|
|
82296
|
+
return;
|
|
82297
|
+
}
|
|
82298
|
+
try {
|
|
82299
|
+
const n = new NotifCtor({ title: `${viewerName ?? "Remote viewer"} \u2014 remote desktop`, body: text8, hasReply: true, replyPlaceholder: "Reply\u2026" });
|
|
82300
|
+
n.on("reply", (_e, reply3) => {
|
|
82301
|
+
const r = typeof reply3 === "string" ? reply3.trim() : "";
|
|
82302
|
+
if (r)
|
|
82303
|
+
rdeskRef?.sendChat(sessionId, r);
|
|
82304
|
+
});
|
|
82305
|
+
n.show();
|
|
82306
|
+
} catch (e) {
|
|
82307
|
+
log9("[rdesk chat] notify failed", e instanceof Error ? e.message : String(e));
|
|
82308
|
+
}
|
|
80915
82309
|
},
|
|
80916
|
-
log: (m) =>
|
|
82310
|
+
log: (m) => log9(m)
|
|
80917
82311
|
});
|
|
80918
82312
|
rdeskRef = rdesk;
|
|
80919
82313
|
rdeskManager = rdesk;
|
|
@@ -80922,7 +82316,7 @@ async function runDaemon(opts = {}) {
|
|
|
80922
82316
|
token: async () => accessToken,
|
|
80923
82317
|
deviceId: c.deviceId,
|
|
80924
82318
|
projectDir: daemonWorkspaceDir(),
|
|
80925
|
-
log: (...args) =>
|
|
82319
|
+
log: (...args) => log9("chat-agent", ...args.map((a) => String(a)))
|
|
80926
82320
|
});
|
|
80927
82321
|
const hooks2 = {
|
|
80928
82322
|
deliverToRenderer: () => {
|
|
@@ -80939,7 +82333,7 @@ async function runDaemon(opts = {}) {
|
|
|
80939
82333
|
getRdeskClientWebContents()?.send("hostlink:own", own2);
|
|
80940
82334
|
},
|
|
80941
82335
|
notify: (n) => {
|
|
80942
|
-
|
|
82336
|
+
log9("notify", JSON.stringify(n));
|
|
80943
82337
|
getRdeskClientWebContents()?.send("ccb:notify", n);
|
|
80944
82338
|
},
|
|
80945
82339
|
// No renderer, but DO forward remote change feeds to the macOS File Provider extension
|
|
@@ -80952,6 +82346,7 @@ async function runDaemon(opts = {}) {
|
|
|
80952
82346
|
onHostsChanged: () => broadcastFileProviderHostsChanged(),
|
|
80953
82347
|
pushTransferEvent: () => {
|
|
80954
82348
|
},
|
|
82349
|
+
onDrop: (msg) => drops.onFrame(msg),
|
|
80955
82350
|
pushTerm: () => {
|
|
80956
82351
|
},
|
|
80957
82352
|
// Remote-desktop host: a viewer drives an rdesk session on this machine.
|
|
@@ -81003,13 +82398,13 @@ async function runDaemon(opts = {}) {
|
|
|
81003
82398
|
getPeerIdentityPub: (d) => getPeerIdentityPub({ apiOrigin: c.apiOrigin, accessToken, deviceId: d })
|
|
81004
82399
|
};
|
|
81005
82400
|
} catch (err2) {
|
|
81006
|
-
|
|
82401
|
+
log9("e2e responder init failed (staying plaintext)", err2.message);
|
|
81007
82402
|
}
|
|
81008
82403
|
}
|
|
81009
82404
|
startControlLink({ deviceId: c.deviceId, power: createNodePowerController(), tokenSource, version: VERSION, e2e }, hooks2);
|
|
81010
|
-
startFileProviderBridge(
|
|
81011
|
-
startAutofillBridge(
|
|
81012
|
-
|
|
82405
|
+
startFileProviderBridge(log9);
|
|
82406
|
+
startAutofillBridge(log9);
|
|
82407
|
+
log9(`started \u2014 device ${c.deviceId}, user ${c.userId}, relay ${c.backendOrigin}, local :${localInfo?.localPort ?? "off"}, version ${VERSION}`);
|
|
81013
82408
|
await refresh();
|
|
81014
82409
|
timer = setInterval(() => {
|
|
81015
82410
|
void refresh();
|
|
@@ -81034,14 +82429,14 @@ var CHUNK_BYTES = 256 * 1024;
|
|
|
81034
82429
|
import { platform as platform3 } from "node:os";
|
|
81035
82430
|
|
|
81036
82431
|
// ../../packages/host-core/dist/service/launchd.js
|
|
81037
|
-
import { writeFileSync as writeFileSync26, existsSync as existsSync29, mkdirSync as mkdirSync27, unlinkSync as
|
|
81038
|
-
import { dirname as
|
|
82432
|
+
import { writeFileSync as writeFileSync26, existsSync as existsSync29, mkdirSync as mkdirSync27, unlinkSync as unlinkSync5 } from "node:fs";
|
|
82433
|
+
import { dirname as dirname24, join as join55 } from "node:path";
|
|
81039
82434
|
import { homedir as homedir8, userInfo } from "node:os";
|
|
81040
82435
|
|
|
81041
82436
|
// ../../packages/host-core/dist/service/exec.js
|
|
81042
|
-
import { execFile as
|
|
81043
|
-
import { promisify as
|
|
81044
|
-
var run3 =
|
|
82437
|
+
import { execFile as execFile7, spawn as spawn10 } from "node:child_process";
|
|
82438
|
+
import { promisify as promisify6 } from "node:util";
|
|
82439
|
+
var run3 = promisify6(execFile7);
|
|
81045
82440
|
function runInherit(cmd, args) {
|
|
81046
82441
|
return new Promise((resolve12) => {
|
|
81047
82442
|
const p = spawn10(cmd, args, { stdio: "inherit" });
|
|
@@ -81059,10 +82454,10 @@ function xml(s) {
|
|
|
81059
82454
|
}
|
|
81060
82455
|
function makeLaunchd(instanceId) {
|
|
81061
82456
|
const label = SERVICE_LABEL_BASE + (instanceId ? `.${instanceId}` : "");
|
|
81062
|
-
const plistPath = () =>
|
|
82457
|
+
const plistPath = () => join55(homedir8(), "Library", "LaunchAgents", `${label}.plist`);
|
|
81063
82458
|
const domainTarget = () => `gui/${userInfo().uid}`;
|
|
81064
82459
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
81065
|
-
const logFile = () =>
|
|
82460
|
+
const logFile = () => join55(daemonDir2(), "daemon.log");
|
|
81066
82461
|
const workDirOf = (spec) => spec.workingDirectory ?? daemonDir2();
|
|
81067
82462
|
function buildPlist(spec) {
|
|
81068
82463
|
const args = [spec.exec, ...spec.args].map((a) => ` <string>${xml(a)}</string>`).join("\n");
|
|
@@ -81108,8 +82503,8 @@ ${env}
|
|
|
81108
82503
|
return {
|
|
81109
82504
|
async install(spec) {
|
|
81110
82505
|
mkdirSync27(workDirOf(spec), { recursive: true });
|
|
81111
|
-
mkdirSync27(
|
|
81112
|
-
mkdirSync27(
|
|
82506
|
+
mkdirSync27(dirname24(plistPath()), { recursive: true });
|
|
82507
|
+
mkdirSync27(dirname24(spec.logPath), { recursive: true });
|
|
81113
82508
|
writeFileSync26(plistPath(), buildPlist(spec));
|
|
81114
82509
|
await run3("launchctl", ["bootout", serviceTarget()]).catch(() => {
|
|
81115
82510
|
});
|
|
@@ -81124,7 +82519,7 @@ ${env}
|
|
|
81124
82519
|
});
|
|
81125
82520
|
if (existsSync29(plistPath())) {
|
|
81126
82521
|
try {
|
|
81127
|
-
|
|
82522
|
+
unlinkSync5(plistPath());
|
|
81128
82523
|
} catch {
|
|
81129
82524
|
}
|
|
81130
82525
|
}
|
|
@@ -81160,8 +82555,8 @@ ${env}
|
|
|
81160
82555
|
}
|
|
81161
82556
|
|
|
81162
82557
|
// ../../packages/host-core/dist/service/systemd.js
|
|
81163
|
-
import { writeFileSync as writeFileSync27, existsSync as existsSync30, mkdirSync as mkdirSync28, unlinkSync as
|
|
81164
|
-
import { dirname as
|
|
82558
|
+
import { writeFileSync as writeFileSync27, existsSync as existsSync30, mkdirSync as mkdirSync28, unlinkSync as unlinkSync6 } from "node:fs";
|
|
82559
|
+
import { dirname as dirname25, join as join56 } from "node:path";
|
|
81165
82560
|
import { homedir as homedir9, userInfo as userInfo2 } from "node:os";
|
|
81166
82561
|
function unitVal(v) {
|
|
81167
82562
|
return v.replace(/%/g, "%%");
|
|
@@ -81172,8 +82567,8 @@ function shq(s) {
|
|
|
81172
82567
|
function makeSystemd(instanceId) {
|
|
81173
82568
|
const unit = `tesseract-host${instanceId ? `-${instanceId}` : ""}.service`;
|
|
81174
82569
|
const unitPath = () => {
|
|
81175
|
-
const base = process.env.XDG_CONFIG_HOME ||
|
|
81176
|
-
return
|
|
82570
|
+
const base = process.env.XDG_CONFIG_HOME || join56(homedir9(), ".config");
|
|
82571
|
+
return join56(base, "systemd", "user", unit);
|
|
81177
82572
|
};
|
|
81178
82573
|
const workDirOf = (spec) => spec.workingDirectory ?? daemonDir2();
|
|
81179
82574
|
function buildUnit(spec) {
|
|
@@ -81201,8 +82596,8 @@ WantedBy=default.target
|
|
|
81201
82596
|
return {
|
|
81202
82597
|
async install(spec) {
|
|
81203
82598
|
mkdirSync28(workDirOf(spec), { recursive: true });
|
|
81204
|
-
mkdirSync28(
|
|
81205
|
-
mkdirSync28(
|
|
82599
|
+
mkdirSync28(dirname25(unitPath()), { recursive: true });
|
|
82600
|
+
mkdirSync28(dirname25(spec.logPath), { recursive: true });
|
|
81206
82601
|
writeFileSync27(unitPath(), buildUnit(spec));
|
|
81207
82602
|
await run3("loginctl", ["enable-linger", userInfo2().username]).catch(() => {
|
|
81208
82603
|
});
|
|
@@ -81217,7 +82612,7 @@ WantedBy=default.target
|
|
|
81217
82612
|
});
|
|
81218
82613
|
if (existsSync30(unitPath())) {
|
|
81219
82614
|
try {
|
|
81220
|
-
|
|
82615
|
+
unlinkSync6(unitPath());
|
|
81221
82616
|
} catch {
|
|
81222
82617
|
}
|
|
81223
82618
|
}
|
|
@@ -81246,12 +82641,12 @@ WantedBy=default.target
|
|
|
81246
82641
|
}
|
|
81247
82642
|
|
|
81248
82643
|
// ../../packages/host-core/dist/service/windows.js
|
|
81249
|
-
import { writeFileSync as writeFileSync28, existsSync as existsSync31, mkdirSync as mkdirSync29, unlinkSync as
|
|
81250
|
-
import { dirname as
|
|
82644
|
+
import { writeFileSync as writeFileSync28, existsSync as existsSync31, mkdirSync as mkdirSync29, unlinkSync as unlinkSync7 } from "node:fs";
|
|
82645
|
+
import { dirname as dirname26, join as join57 } from "node:path";
|
|
81251
82646
|
function makeWindowsTask(instanceId) {
|
|
81252
82647
|
const task = `Fineguide\\TesseractHost${instanceId ? `-${instanceId}` : ""}`;
|
|
81253
|
-
const wrapperPath = () =>
|
|
81254
|
-
const logFile = () =>
|
|
82648
|
+
const wrapperPath = () => join57(daemonDir2(), `run-host${instanceId ? `-${instanceId}` : ""}.cmd`);
|
|
82649
|
+
const logFile = () => join57(daemonDir2(), "daemon.log");
|
|
81255
82650
|
const oneLine2 = (s) => s.replace(/[\r\n]+/g, " ");
|
|
81256
82651
|
const envVal = (v) => oneLine2(v).replace(/%/g, "%%").replace(/"/g, "");
|
|
81257
82652
|
const cmdArg = (a) => `"${oneLine2(a).replace(/"/g, "")}"`;
|
|
@@ -81268,8 +82663,8 @@ start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
|
|
|
81268
82663
|
return {
|
|
81269
82664
|
async install(spec) {
|
|
81270
82665
|
mkdirSync29(spec.workingDirectory ?? daemonDir2(), { recursive: true });
|
|
81271
|
-
mkdirSync29(
|
|
81272
|
-
mkdirSync29(
|
|
82666
|
+
mkdirSync29(dirname26(spec.logPath), { recursive: true });
|
|
82667
|
+
mkdirSync29(dirname26(wrapperPath()), { recursive: true });
|
|
81273
82668
|
writeFileSync28(wrapperPath(), buildWrapper(spec));
|
|
81274
82669
|
await run3("schtasks", ["/Create", "/TN", task, "/SC", "ONLOGON", "/RL", "LIMITED", "/F", "/TR", `"${wrapperPath()}"`]);
|
|
81275
82670
|
await run3("schtasks", ["/Run", "/TN", task]).catch(() => {
|
|
@@ -81280,7 +82675,7 @@ start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
|
|
|
81280
82675
|
});
|
|
81281
82676
|
if (existsSync31(wrapperPath())) {
|
|
81282
82677
|
try {
|
|
81283
|
-
|
|
82678
|
+
unlinkSync7(wrapperPath());
|
|
81284
82679
|
} catch {
|
|
81285
82680
|
}
|
|
81286
82681
|
}
|
|
@@ -81334,9 +82729,9 @@ function getServiceController(instanceId) {
|
|
|
81334
82729
|
|
|
81335
82730
|
// src/auth.ts
|
|
81336
82731
|
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
81337
|
-
import { randomUUID as
|
|
82732
|
+
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
81338
82733
|
import { existsSync as existsSync32, rmSync as rmSync8 } from "node:fs";
|
|
81339
|
-
import { join as
|
|
82734
|
+
import { join as join58 } from "node:path";
|
|
81340
82735
|
|
|
81341
82736
|
// src/config.ts
|
|
81342
82737
|
import { hostname as hostname5 } from "node:os";
|
|
@@ -81476,7 +82871,7 @@ async function login(opts) {
|
|
|
81476
82871
|
process.exit(1);
|
|
81477
82872
|
}
|
|
81478
82873
|
}
|
|
81479
|
-
const deviceId2 = readCredentials()?.deviceId ?? process.env.CCB_DEVICE_ID ??
|
|
82874
|
+
const deviceId2 = readCredentials()?.deviceId ?? process.env.CCB_DEVICE_ID ?? randomUUID18().slice(0, 8);
|
|
81480
82875
|
const label = opts.name ?? defaultLabel();
|
|
81481
82876
|
if (opts.token) {
|
|
81482
82877
|
const dt = await fetchJson(`${api}/auth/device-token`, {
|
|
@@ -81537,7 +82932,7 @@ async function logout() {
|
|
|
81537
82932
|
}
|
|
81538
82933
|
for (const f of ["credentials.json", "status.json", "daemon.lock"]) {
|
|
81539
82934
|
try {
|
|
81540
|
-
const p =
|
|
82935
|
+
const p = join58(daemonDir2(), f);
|
|
81541
82936
|
if (existsSync32(p)) rmSync8(p);
|
|
81542
82937
|
} catch {
|
|
81543
82938
|
}
|
|
@@ -81584,7 +82979,7 @@ function isNewer(latest, current) {
|
|
|
81584
82979
|
for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] > b[i];
|
|
81585
82980
|
return false;
|
|
81586
82981
|
}
|
|
81587
|
-
function
|
|
82982
|
+
function log8(...args) {
|
|
81588
82983
|
console.log(`[self-update ${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args);
|
|
81589
82984
|
}
|
|
81590
82985
|
async function fetchLatest() {
|
|
@@ -81630,25 +83025,25 @@ function verifyNpmSignature(version2, integrity, sig) {
|
|
|
81630
83025
|
function installVersion(target) {
|
|
81631
83026
|
return new Promise((resolve12) => {
|
|
81632
83027
|
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
81633
|
-
|
|
83028
|
+
log8(`installing tesseract-agent@${target} \u2026`);
|
|
81634
83029
|
let child;
|
|
81635
83030
|
try {
|
|
81636
83031
|
child = spawn11(npm, ["install", "-g", `tesseract-agent@${target}`], { stdio: ["ignore", "inherit", "inherit"] });
|
|
81637
83032
|
} catch (e) {
|
|
81638
|
-
|
|
83033
|
+
log8("npm spawn threw:", e instanceof Error ? e.message : String(e));
|
|
81639
83034
|
resolve12(false);
|
|
81640
83035
|
return;
|
|
81641
83036
|
}
|
|
81642
83037
|
child.on("error", (e) => {
|
|
81643
|
-
|
|
83038
|
+
log8("npm not runnable:", e.message);
|
|
81644
83039
|
resolve12(false);
|
|
81645
83040
|
});
|
|
81646
83041
|
child.on("close", (code) => {
|
|
81647
83042
|
if (code === 0) {
|
|
81648
|
-
|
|
83043
|
+
log8(`installed ${target}`);
|
|
81649
83044
|
resolve12(true);
|
|
81650
83045
|
} else {
|
|
81651
|
-
|
|
83046
|
+
log8(`npm install exited ${code} \u2014 staying on the current version`);
|
|
81652
83047
|
resolve12(false);
|
|
81653
83048
|
}
|
|
81654
83049
|
});
|
|
@@ -81661,15 +83056,15 @@ async function checkOnce(current) {
|
|
|
81661
83056
|
try {
|
|
81662
83057
|
const latest = await fetchLatest();
|
|
81663
83058
|
if (!latest || !isNewer(latest, current)) return;
|
|
81664
|
-
|
|
83059
|
+
log8(`update available: ${current} \u2192 ${latest}`);
|
|
81665
83060
|
const dist = await fetchVersionDist(latest);
|
|
81666
83061
|
if (!verifyNpmSignature(latest, dist?.integrity, dist?.sig)) {
|
|
81667
|
-
|
|
83062
|
+
log8(`REFUSING update to ${latest}: registry signature did not verify \u2014 staying on ${current}`);
|
|
81668
83063
|
return;
|
|
81669
83064
|
}
|
|
81670
|
-
|
|
83065
|
+
log8(`registry signature verified for ${latest} (keyid ${dist?.keyid ?? "?"})`);
|
|
81671
83066
|
if (await installVersion(latest)) {
|
|
81672
|
-
|
|
83067
|
+
log8("restarting on the new version (supervisor re-spawns) \u2026");
|
|
81673
83068
|
process.exit(RESTART_EXIT_CODE);
|
|
81674
83069
|
}
|
|
81675
83070
|
} finally {
|
|
@@ -81678,11 +83073,11 @@ async function checkOnce(current) {
|
|
|
81678
83073
|
}
|
|
81679
83074
|
function startSelfUpdater(current) {
|
|
81680
83075
|
if (disabled()) {
|
|
81681
|
-
|
|
83076
|
+
log8("disabled via TESSERACT_AUTO_UPDATE/CCB_AUTO_UPDATE");
|
|
81682
83077
|
return;
|
|
81683
83078
|
}
|
|
81684
83079
|
if (!isReleaseVersion(current)) {
|
|
81685
|
-
|
|
83080
|
+
log8(`skipped \u2014 non-release version "${current}"`);
|
|
81686
83081
|
return;
|
|
81687
83082
|
}
|
|
81688
83083
|
const first = setTimeout(() => {
|
|
@@ -81696,7 +83091,7 @@ function startSelfUpdater(current) {
|
|
|
81696
83091
|
}
|
|
81697
83092
|
|
|
81698
83093
|
// src/service.ts
|
|
81699
|
-
import { join as
|
|
83094
|
+
import { join as join59 } from "node:path";
|
|
81700
83095
|
import { platform as platform4 } from "node:os";
|
|
81701
83096
|
import { existsSync as existsSync33, readFileSync as readFileSync31 } from "node:fs";
|
|
81702
83097
|
function controller(instanceId) {
|
|
@@ -81711,7 +83106,7 @@ function buildSpec(cliEntry) {
|
|
|
81711
83106
|
exec: process.execPath,
|
|
81712
83107
|
args: [cliEntry, "run"],
|
|
81713
83108
|
env: { CCB_DAEMON_DIR: daemonDir2() },
|
|
81714
|
-
logPath:
|
|
83109
|
+
logPath: join59(daemonDir2(), "daemon.log")
|
|
81715
83110
|
};
|
|
81716
83111
|
}
|
|
81717
83112
|
var isAlive = (pid) => {
|
|
@@ -81723,7 +83118,7 @@ var isAlive = (pid) => {
|
|
|
81723
83118
|
}
|
|
81724
83119
|
};
|
|
81725
83120
|
function runningDaemonInfo() {
|
|
81726
|
-
const lock =
|
|
83121
|
+
const lock = join59(daemonDir2(), "daemon.lock");
|
|
81727
83122
|
try {
|
|
81728
83123
|
if (!existsSync33(lock)) return null;
|
|
81729
83124
|
const pid = Number(readFileSync31(lock, "utf-8").trim());
|
|
@@ -81762,7 +83157,7 @@ async function logs(instanceId, follow) {
|
|
|
81762
83157
|
// src/cli.ts
|
|
81763
83158
|
function pkgVersion() {
|
|
81764
83159
|
try {
|
|
81765
|
-
const p =
|
|
83160
|
+
const p = join60(dirname27(fileURLToPath4(import.meta.url)), "..", "package.json");
|
|
81766
83161
|
return JSON.parse(readFileSync32(p, "utf8")).version ?? "0.0.0";
|
|
81767
83162
|
} catch {
|
|
81768
83163
|
return "0.0.0";
|
|
@@ -81955,7 +83350,7 @@ async function linkCommand(rest, flags) {
|
|
|
81955
83350
|
process.exit(1);
|
|
81956
83351
|
}
|
|
81957
83352
|
async function captureClaudeSubscription(flags) {
|
|
81958
|
-
const home = mkdtempSync2(
|
|
83353
|
+
const home = mkdtempSync2(join60(tmpdir4(), "tesseract-claude-login-"));
|
|
81959
83354
|
const binary = typeof flags["claude-path"] === "string" ? flags["claude-path"] : process.env.CCB_ENGINE_PATH_CLAUDE_CODE || "claude";
|
|
81960
83355
|
const args = ["auth", "login", "--claudeai"];
|
|
81961
83356
|
if (typeof flags.email === "string") args.push("--email", flags.email);
|
|
@@ -81972,7 +83367,7 @@ async function captureClaudeSubscription(flags) {
|
|
|
81972
83367
|
child.once("close", (exitCode, signal) => resolveCode(signal ? 1 : exitCode ?? 1));
|
|
81973
83368
|
});
|
|
81974
83369
|
if (code !== 0) throw new Error(`Claude sign-in did not complete (exit ${code}).`);
|
|
81975
|
-
const file2 =
|
|
83370
|
+
const file2 = join60(home, ".credentials.json");
|
|
81976
83371
|
if (existsSync34(file2)) return readFileSync32(file2, "utf8");
|
|
81977
83372
|
if (process.platform === "darwin") {
|
|
81978
83373
|
const value = execFileSync5("security", [
|