@dashup/connector 0.1.7 → 0.1.9
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/README.md +6 -20
- package/dist/index.js +389 -597
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -42,7 +42,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
42
42
|
"use strict";
|
|
43
43
|
var fs = __require("fs");
|
|
44
44
|
var Url = __require("url");
|
|
45
|
-
var
|
|
45
|
+
var spawn6 = __require("child_process").spawn;
|
|
46
46
|
module.exports = XMLHttpRequest3;
|
|
47
47
|
XMLHttpRequest3.XMLHttpRequest = XMLHttpRequest3;
|
|
48
48
|
function XMLHttpRequest3(opts) {
|
|
@@ -338,7 +338,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
338
338
|
var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
|
|
339
339
|
fs.writeFileSync(syncFile, "", "utf8");
|
|
340
340
|
var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
|
|
341
|
-
var syncProc =
|
|
341
|
+
var syncProc = spawn6(process.argv[0], ["-e", execString]);
|
|
342
342
|
var statusText;
|
|
343
343
|
while (fs.existsSync(syncFile)) {
|
|
344
344
|
}
|
|
@@ -1849,7 +1849,7 @@ var require_dist = __commonJS({
|
|
|
1849
1849
|
var require_eventemitter3 = __commonJS({
|
|
1850
1850
|
"../../node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index.js"(exports, module) {
|
|
1851
1851
|
"use strict";
|
|
1852
|
-
var
|
|
1852
|
+
var has = Object.prototype.hasOwnProperty;
|
|
1853
1853
|
var prefix2 = "~";
|
|
1854
1854
|
function Events() {
|
|
1855
1855
|
}
|
|
@@ -1884,7 +1884,7 @@ var require_eventemitter3 = __commonJS({
|
|
|
1884
1884
|
var names = [], events, name;
|
|
1885
1885
|
if (this._eventsCount === 0) return names;
|
|
1886
1886
|
for (name in events = this._events) {
|
|
1887
|
-
if (
|
|
1887
|
+
if (has.call(events, name)) names.push(prefix2 ? name.slice(1) : name);
|
|
1888
1888
|
}
|
|
1889
1889
|
if (Object.getOwnPropertySymbols) {
|
|
1890
1890
|
return names.concat(Object.getOwnPropertySymbols(events));
|
|
@@ -2726,6 +2726,7 @@ var PRODUCT = {
|
|
|
2726
2726
|
var PAIRING_TTL_MS = 5 * 6e4;
|
|
2727
2727
|
var PROTOCOL_VERSION = 1;
|
|
2728
2728
|
var MAX_ENVELOPE_BYTES = 8 * 1024 * 1024;
|
|
2729
|
+
var HISTORY_PAGE_ITEMS = 30;
|
|
2729
2730
|
var HISTORY_TEXT_CHARS = 4e3;
|
|
2730
2731
|
var HISTORY_PAGE_BYTES = 256 * 1024;
|
|
2731
2732
|
var HISTORY_BATCH_ITEMS = 50;
|
|
@@ -3615,8 +3616,6 @@ function loadConfig() {
|
|
|
3615
3616
|
api: PRODUCT.api,
|
|
3616
3617
|
secret: "",
|
|
3617
3618
|
pairs: {},
|
|
3618
|
-
screen: false,
|
|
3619
|
-
control: false,
|
|
3620
3619
|
outbox: [],
|
|
3621
3620
|
localSecret: ""
|
|
3622
3621
|
};
|
|
@@ -4017,7 +4016,7 @@ import * as ui from "@clack/prompts";
|
|
|
4017
4016
|
import { z } from "zod";
|
|
4018
4017
|
|
|
4019
4018
|
// package.json
|
|
4020
|
-
var version = "0.1.
|
|
4019
|
+
var version = "0.1.9";
|
|
4021
4020
|
|
|
4022
4021
|
// src/pair.ts
|
|
4023
4022
|
var Invitation = z.object({
|
|
@@ -4147,284 +4146,26 @@ async function link(config2, account) {
|
|
|
4147
4146
|
return true;
|
|
4148
4147
|
}
|
|
4149
4148
|
|
|
4150
|
-
// src/viewer.ts
|
|
4151
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
4152
|
-
import { fileURLToPath } from "node:url";
|
|
4153
|
-
import { createInterface as createInterface2 } from "node:readline";
|
|
4154
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
4155
|
-
|
|
4156
|
-
// src/screen.ts
|
|
4157
|
-
import { promisify as promisify2 } from "node:util";
|
|
4158
|
-
import { execFile as execFile2, spawnSync } from "node:child_process";
|
|
4159
|
-
var run = promisify2(execFile2);
|
|
4160
|
-
var powershell = (source) => run("powershell.exe", [
|
|
4161
|
-
"-NoProfile",
|
|
4162
|
-
"-NonInteractive",
|
|
4163
|
-
"-EncodedCommand",
|
|
4164
|
-
Buffer.from(source, "utf16le").toString("base64")
|
|
4165
|
-
]);
|
|
4166
|
-
var psString = (value2) => `[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${Buffer.from(value2).toString("base64")}'))`;
|
|
4167
|
-
function has(command2) {
|
|
4168
|
-
return spawnSync(process.platform === "win32" ? "where.exe" : "which", [command2], { stdio: "ignore" }).status === 0;
|
|
4169
|
-
}
|
|
4170
|
-
function controlAvailable() {
|
|
4171
|
-
return process.platform === "darwin" || process.platform === "win32" || has(process.env.WAYLAND_DISPLAY ? "ydotool" : "xdotool");
|
|
4172
|
-
}
|
|
4173
|
-
var KEYS = {
|
|
4174
|
-
a: { x11: "a", linux: 30, mac: "a", windows: "a" },
|
|
4175
|
-
c: { x11: "c", linux: 46, mac: "c", windows: "c" },
|
|
4176
|
-
v: { x11: "v", linux: 47, mac: "v", windows: "v" },
|
|
4177
|
-
z: { x11: "z", linux: 44, mac: "z", windows: "z" },
|
|
4178
|
-
Tab: { x11: "Tab", linux: 15, mac: 48, windows: "{TAB}" },
|
|
4179
|
-
Enter: { x11: "Return", linux: 28, mac: 36, windows: "{ENTER}" },
|
|
4180
|
-
Escape: { x11: "Escape", linux: 1, mac: 53, windows: "{ESC}" },
|
|
4181
|
-
ArrowUp: { x11: "Up", linux: 103, mac: 126, windows: "{UP}" },
|
|
4182
|
-
Backspace: { x11: "BackSpace", linux: 14, mac: 51, windows: "{BACKSPACE}" },
|
|
4183
|
-
ArrowDown: { x11: "Down", linux: 108, mac: 125, windows: "{DOWN}" },
|
|
4184
|
-
ArrowLeft: { x11: "Left", linux: 105, mac: 123, windows: "{LEFT}" },
|
|
4185
|
-
ArrowRight: { x11: "Right", linux: 106, mac: 124, windows: "{RIGHT}" }
|
|
4186
|
-
};
|
|
4187
|
-
var MODIFIERS = {
|
|
4188
|
-
alt: { x11: "alt", linux: 56, mac: "option down", windows: "%" },
|
|
4189
|
-
meta: { x11: "super", linux: 125, mac: "command down", windows: "" },
|
|
4190
|
-
shift: { x11: "shift", linux: 42, mac: "shift down", windows: "+" },
|
|
4191
|
-
control: { x11: "ctrl", linux: 29, mac: "control down", windows: "^" }
|
|
4192
|
-
};
|
|
4193
|
-
var SCROLL_LIMIT = 10;
|
|
4194
|
-
var WINDOWS_INPUT = `Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class DashupInput { [DllImport("user32.dll")] public static extern bool SetCursorPos(int x,int y); [DllImport("user32.dll")] public static extern void mouse_event(uint f,uint x,uint y,int d,UIntPtr e); [DllImport("user32.dll")] public static extern void keybd_event(byte v,byte s,uint f,UIntPtr e); }'; `;
|
|
4195
|
-
async function controlScreen(input) {
|
|
4196
|
-
if (input.kind === "click" && (!Number.isInteger(input.x) || !Number.isInteger(input.y) || input.x < -32768 || input.y < -32768 || input.x > 32767 || input.y > 32767))
|
|
4197
|
-
throw new Error("Invalid screen coordinates");
|
|
4198
|
-
if (input.kind === "text" && (typeof input.text !== "string" || input.text.length > 16e3))
|
|
4199
|
-
throw new Error("Text is too long");
|
|
4200
|
-
if (input.kind === "scroll" && (!Number.isInteger(input.dx) || !Number.isInteger(input.dy) || Math.abs(input.dx) > SCROLL_LIMIT || Math.abs(input.dy) > SCROLL_LIMIT))
|
|
4201
|
-
throw new Error("Invalid scroll distance");
|
|
4202
|
-
const key = input.kind === "key" && Object.hasOwn(KEYS, input.key) ? KEYS[input.key] : void 0;
|
|
4203
|
-
if (input.kind === "key" && !key) throw new Error("This key is not supported");
|
|
4204
|
-
const modifiers = input.kind === "key" ? input.modifiers ?? [] : [];
|
|
4205
|
-
if (!Array.isArray(modifiers) || modifiers.length > 4 || modifiers.some((modifier) => !Object.hasOwn(MODIFIERS, modifier)))
|
|
4206
|
-
throw new Error("This modifier is not supported");
|
|
4207
|
-
const chord = [...new Set(modifiers)].map((modifier) => MODIFIERS[modifier]);
|
|
4208
|
-
if (process.platform === "darwin") {
|
|
4209
|
-
if (input.kind === "scroll") {
|
|
4210
|
-
await run("osascript", [
|
|
4211
|
-
"-l",
|
|
4212
|
-
"JavaScript",
|
|
4213
|
-
"-e",
|
|
4214
|
-
`ObjC.import('ApplicationServices'); const event = $.CGEventCreateScrollWheelEvent2(null, 0, 2, ${-input.dy}, ${-input.dx}, 0); if (!event) throw Error('Could not create scroll event'); $.CGEventPost(0, event); $.CFRelease(event);`
|
|
4215
|
-
]);
|
|
4216
|
-
return;
|
|
4217
|
-
}
|
|
4218
|
-
const command2 = input.kind === "click" ? `click at {${input.x}, ${input.y}}` : input.kind === "key" ? `${typeof key.mac === "number" ? "key code " + key.mac : "keystroke " + JSON.stringify(key.mac)}${chord.length ? " using {" + chord.map((modifier) => modifier.mac).join(", ") + "}" : ""}` : `keystroke ${JSON.stringify(input.text)}`;
|
|
4219
|
-
await run("osascript", ["-e", `tell application "System Events" to ${command2}`]);
|
|
4220
|
-
} else if (process.platform === "win32") {
|
|
4221
|
-
if (input.kind === "click" || input.kind === "scroll") {
|
|
4222
|
-
const mouse = (flags, data = 0) => `[DashupInput]::mouse_event(${flags},0,0,${data},[UIntPtr]::Zero); `;
|
|
4223
|
-
await powershell(
|
|
4224
|
-
WINDOWS_INPUT + (input.kind === "click" ? `[DashupInput]::SetCursorPos(${input.x},${input.y}); ` + mouse(2) + mouse(4) : mouse(2048, -input.dy * 120) + mouse(4096, input.dx * 120))
|
|
4225
|
-
);
|
|
4226
|
-
} else {
|
|
4227
|
-
const text3 = input.kind === "key" ? chord.map((modifier) => modifier.windows).join("") + key.windows : input.text.replace(/[+^%~(){}\[\]]/g, "{$&}");
|
|
4228
|
-
const send = `Add-Type -AssemblyName System.Windows.Forms; [Windows.Forms.SendKeys]::SendWait(${psString(text3)})`;
|
|
4229
|
-
await powershell(
|
|
4230
|
-
modifiers.includes("meta") ? WINDOWS_INPUT + `[DashupInput]::keybd_event(91,0,0,[UIntPtr]::Zero); try { ${send} } finally { [DashupInput]::keybd_event(91,0,2,[UIntPtr]::Zero) }` : send
|
|
4231
|
-
);
|
|
4232
|
-
}
|
|
4233
|
-
} else if (process.env.WAYLAND_DISPLAY) {
|
|
4234
|
-
if (input.kind === "text") await run("ydotool", ["type", "--", input.text]);
|
|
4235
|
-
else if (input.kind === "key")
|
|
4236
|
-
await run("ydotool", [
|
|
4237
|
-
"key",
|
|
4238
|
-
...chord.map((modifier) => `${modifier.linux}:1`),
|
|
4239
|
-
`${key.linux}:1`,
|
|
4240
|
-
`${key.linux}:0`,
|
|
4241
|
-
...[...chord].reverse().map((modifier) => `${modifier.linux}:0`)
|
|
4242
|
-
]);
|
|
4243
|
-
else if (input.kind === "scroll")
|
|
4244
|
-
await run("ydotool", [
|
|
4245
|
-
"mousemove",
|
|
4246
|
-
"--wheel",
|
|
4247
|
-
"-x",
|
|
4248
|
-
String(input.dx),
|
|
4249
|
-
"-y",
|
|
4250
|
-
String(-input.dy)
|
|
4251
|
-
]);
|
|
4252
|
-
else {
|
|
4253
|
-
await run("ydotool", [
|
|
4254
|
-
"mousemove",
|
|
4255
|
-
"--absolute",
|
|
4256
|
-
"-x",
|
|
4257
|
-
String(input.x),
|
|
4258
|
-
"-y",
|
|
4259
|
-
String(input.y)
|
|
4260
|
-
]);
|
|
4261
|
-
await run("ydotool", ["click", "0xC0"]);
|
|
4262
|
-
}
|
|
4263
|
-
} else {
|
|
4264
|
-
if (input.kind === "scroll") {
|
|
4265
|
-
for (const [distance, negative, positive] of [
|
|
4266
|
-
[input.dy, 4, 5],
|
|
4267
|
-
[input.dx, 6, 7]
|
|
4268
|
-
])
|
|
4269
|
-
if (distance)
|
|
4270
|
-
await run("xdotool", [
|
|
4271
|
-
"click",
|
|
4272
|
-
"--clearmodifiers",
|
|
4273
|
-
"--repeat",
|
|
4274
|
-
String(Math.abs(distance)),
|
|
4275
|
-
"--delay",
|
|
4276
|
-
"0",
|
|
4277
|
-
String(distance < 0 ? negative : positive)
|
|
4278
|
-
]);
|
|
4279
|
-
return;
|
|
4280
|
-
}
|
|
4281
|
-
await run(
|
|
4282
|
-
"xdotool",
|
|
4283
|
-
input.kind === "click" ? ["mousemove", String(input.x), String(input.y), "click", "1"] : input.kind === "key" ? [
|
|
4284
|
-
"key",
|
|
4285
|
-
"--clearmodifiers",
|
|
4286
|
-
[...chord.map((modifier) => modifier.x11), key.x11].join("+")
|
|
4287
|
-
] : ["type", "--clearmodifiers", "--", input.text]
|
|
4288
|
-
);
|
|
4289
|
-
}
|
|
4290
|
-
}
|
|
4291
|
-
|
|
4292
|
-
// src/viewer.ts
|
|
4293
|
-
var DESKTOP_BIN = process.env.DASHUP_DESKTOP_BIN ?? fileURLToPath(
|
|
4294
|
-
new URL(
|
|
4295
|
-
`../../../packages/desktop/target/release/dashup-desktop${process.platform === "win32" ? ".exe" : ""}`,
|
|
4296
|
-
import.meta.url
|
|
4297
|
-
)
|
|
4298
|
-
);
|
|
4299
|
-
var Viewer = class _Viewer {
|
|
4300
|
-
constructor(config2, isActive, send) {
|
|
4301
|
-
this.config = config2;
|
|
4302
|
-
this.isActive = isActive;
|
|
4303
|
-
this.send = send;
|
|
4304
|
-
}
|
|
4305
|
-
config;
|
|
4306
|
-
isActive;
|
|
4307
|
-
send;
|
|
4308
|
-
sessions = /* @__PURE__ */ new Map();
|
|
4309
|
-
process = null;
|
|
4310
|
-
pending = null;
|
|
4311
|
-
static available() {
|
|
4312
|
-
return existsSync2(DESKTOP_BIN);
|
|
4313
|
-
}
|
|
4314
|
-
async start(pair, projectId, session) {
|
|
4315
|
-
if (!this.config.screen || !this.isActive(pair))
|
|
4316
|
-
throw new Error("Screen access is no longer enabled for this pairing");
|
|
4317
|
-
if (!_Viewer.available())
|
|
4318
|
-
throw new Error("Build the Dashup desktop viewer helper on this computer first");
|
|
4319
|
-
if (this.sessions.has(session) || this.pending?.id === session)
|
|
4320
|
-
throw new Error("Screen session already exists");
|
|
4321
|
-
const previous = [...this.sessions];
|
|
4322
|
-
this.close();
|
|
4323
|
-
const pending = { pair, projectId, id: session };
|
|
4324
|
-
this.pending = pending;
|
|
4325
|
-
try {
|
|
4326
|
-
for (const [id, current] of previous) await this.sendClose(id, current);
|
|
4327
|
-
if (this.pending !== pending) throw new Error("Screen request was cancelled");
|
|
4328
|
-
const { iceServers } = await api(this.config, "/dashup/ice");
|
|
4329
|
-
if (this.pending !== pending || !this.config.screen || !this.isActive(pair))
|
|
4330
|
-
throw new Error("Screen access is no longer enabled for this pairing");
|
|
4331
|
-
const child = spawn2(DESKTOP_BIN, [], { stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
4332
|
-
this.process = child;
|
|
4333
|
-
let events = Promise.resolve();
|
|
4334
|
-
createInterface2({ input: child.stdout }).on("error", () => this.failed(child)).on("line", (line) => {
|
|
4335
|
-
events = events.then(() => {
|
|
4336
|
-
if (this.process === child) return this.receive(line);
|
|
4337
|
-
}).catch(() => {
|
|
4338
|
-
process.stderr.write("Desktop viewer event failed\n");
|
|
4339
|
-
});
|
|
4340
|
-
});
|
|
4341
|
-
child.stderr.resume();
|
|
4342
|
-
child.stdin.on("error", () => this.failed(child));
|
|
4343
|
-
child.once("error", () => this.failed(child));
|
|
4344
|
-
child.once("exit", () => this.failed(child));
|
|
4345
|
-
this.sessions.set(session, { pair, projectId });
|
|
4346
|
-
this.write({ kind: "start", session, iceServers });
|
|
4347
|
-
} finally {
|
|
4348
|
-
if (this.pending === pending) this.pending = null;
|
|
4349
|
-
}
|
|
4350
|
-
}
|
|
4351
|
-
signal(pair, projectId, session, signal) {
|
|
4352
|
-
const current = this.sessions.get(session);
|
|
4353
|
-
const pending = this.pending?.id === session ? this.pending : null;
|
|
4354
|
-
const owner = current ?? pending;
|
|
4355
|
-
if (!owner || owner.pair.id !== pair.id || owner.projectId !== projectId || !this.config.screen || !this.isActive(pair))
|
|
4356
|
-
throw new Error("Screen session is no longer active");
|
|
4357
|
-
if (signal.kind === "close") this.stop(pair, session);
|
|
4358
|
-
else if (!current) throw new Error("Screen session is not ready");
|
|
4359
|
-
else this.write({ kind: "signal", session, signal });
|
|
4360
|
-
}
|
|
4361
|
-
stop(pair, session) {
|
|
4362
|
-
if (this.pending?.id === session && this.pending.pair.id === pair.id) this.pending = null;
|
|
4363
|
-
if (this.sessions.get(session)?.pair.id !== pair.id) return;
|
|
4364
|
-
this.write({ kind: "stop", session });
|
|
4365
|
-
this.sessions.delete(session);
|
|
4366
|
-
if (!this.sessions.size) this.close();
|
|
4367
|
-
}
|
|
4368
|
-
revoke(pairings) {
|
|
4369
|
-
if (this.pending && !pairings.has(this.pending.pair.id)) this.pending = null;
|
|
4370
|
-
for (const [session, current] of this.sessions)
|
|
4371
|
-
if (!pairings.has(current.pair.id)) this.stop(current.pair, session);
|
|
4372
|
-
}
|
|
4373
|
-
close() {
|
|
4374
|
-
const child = this.process;
|
|
4375
|
-
this.pending = null;
|
|
4376
|
-
this.process = null;
|
|
4377
|
-
this.sessions.clear();
|
|
4378
|
-
child?.kill();
|
|
4379
|
-
}
|
|
4380
|
-
sendClose(sessionId, { pair, projectId }) {
|
|
4381
|
-
return this.send(pair, { type: "signal", projectId, sessionId, signal: { kind: "close" } });
|
|
4382
|
-
}
|
|
4383
|
-
write(value2) {
|
|
4384
|
-
this.process?.stdin.write(JSON.stringify(value2) + "\n");
|
|
4385
|
-
}
|
|
4386
|
-
failed(child) {
|
|
4387
|
-
if (this.process !== child) return;
|
|
4388
|
-
const sessions = [...this.sessions];
|
|
4389
|
-
this.close();
|
|
4390
|
-
for (const [sessionId, current] of sessions)
|
|
4391
|
-
void this.sendClose(sessionId, current).catch(() => process.stderr.write("Desktop viewer closed\n"));
|
|
4392
|
-
}
|
|
4393
|
-
async receive(line) {
|
|
4394
|
-
const event = JSON.parse(line);
|
|
4395
|
-
const current = this.sessions.get(event.session);
|
|
4396
|
-
if (!current || !this.config.screen || !this.isActive(current.pair)) return;
|
|
4397
|
-
if (event.kind === "signal" && event.signal)
|
|
4398
|
-
await this.send(current.pair, {
|
|
4399
|
-
type: "signal",
|
|
4400
|
-
sessionId: event.session,
|
|
4401
|
-
projectId: current.projectId,
|
|
4402
|
-
signal: event.signal
|
|
4403
|
-
});
|
|
4404
|
-
else if (event.kind === "input" && event.input && this.config.control)
|
|
4405
|
-
await controlScreen(event.input);
|
|
4406
|
-
else if (event.kind === "error" || event.state === "failed" || event.state === "closed" || event.state === "disconnected") {
|
|
4407
|
-
this.stop(current.pair, event.session);
|
|
4408
|
-
await this.sendClose(event.session, current);
|
|
4409
|
-
}
|
|
4410
|
-
}
|
|
4411
|
-
};
|
|
4412
|
-
|
|
4413
4149
|
// src/discovery.ts
|
|
4414
4150
|
import { homedir as homedir3 } from "node:os";
|
|
4415
4151
|
import { createHmac } from "node:crypto";
|
|
4416
4152
|
import { basename, dirname as dirname3, join as join3 } from "node:path";
|
|
4417
4153
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
4418
4154
|
import { zstdDecompressSync } from "node:zlib";
|
|
4419
|
-
import { watch, existsSync as
|
|
4155
|
+
import { watch, existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
4420
4156
|
|
|
4421
4157
|
// src/harness/history.ts
|
|
4422
4158
|
import { homedir as homedir2 } from "node:os";
|
|
4423
4159
|
import { DatabaseSync } from "node:sqlite";
|
|
4424
4160
|
import { join as join2, dirname as dirname2, resolve as resolve2, isAbsolute } from "node:path";
|
|
4425
|
-
import { closeSync as closeSync2, existsSync as
|
|
4161
|
+
import { closeSync as closeSync2, existsSync as existsSync2, openSync as openSync2, readSync, realpathSync, readdirSync, statSync } from "node:fs";
|
|
4426
4162
|
|
|
4427
4163
|
// src/harness/session.ts
|
|
4164
|
+
var EARLIER = {
|
|
4165
|
+
id: "earlier",
|
|
4166
|
+
role: "system",
|
|
4167
|
+
text: "Earlier messages stay on your computer."
|
|
4168
|
+
};
|
|
4428
4169
|
var sessionKey = (session) => `${session.harness}\0${session.id}`;
|
|
4429
4170
|
function agentHarness(name) {
|
|
4430
4171
|
if (/claude/i.test(name)) return "claude";
|
|
@@ -4433,12 +4174,14 @@ function agentHarness(name) {
|
|
|
4433
4174
|
return void 0;
|
|
4434
4175
|
}
|
|
4435
4176
|
var MAX_PREVIEW = 64 * 1024;
|
|
4436
|
-
var
|
|
4177
|
+
var NO_PREVIEW = "Review the full request on your computer before approving.";
|
|
4437
4178
|
|
|
4438
4179
|
// src/harness/history.ts
|
|
4439
4180
|
var SAMPLE_BYTES = 128 * 1024;
|
|
4440
4181
|
var READ_BYTES = 64 * 1024;
|
|
4441
4182
|
var TITLE_LIMIT = 120;
|
|
4183
|
+
var LATEST_BYTES = 8 * 1024 * 1024;
|
|
4184
|
+
var JOURNALS = 16;
|
|
4442
4185
|
var journals = /* @__PURE__ */ new Map();
|
|
4443
4186
|
var metadata = /* @__PURE__ */ new Map();
|
|
4444
4187
|
function object(value2) {
|
|
@@ -4475,10 +4218,10 @@ function readRange(path, start, length) {
|
|
|
4475
4218
|
closeSync2(fd);
|
|
4476
4219
|
}
|
|
4477
4220
|
}
|
|
4478
|
-
function
|
|
4221
|
+
function described(path, until, describe2) {
|
|
4479
4222
|
const stamp = statSync(path);
|
|
4480
4223
|
const saved = metadata.get(path);
|
|
4481
|
-
if (saved && unchanged(saved.stamp, stamp)) return saved.
|
|
4224
|
+
if (saved && unchanged(saved.stamp, stamp)) return saved.value;
|
|
4482
4225
|
let head = readRange(path, 0, Math.min(stamp.size, SAMPLE_BYTES));
|
|
4483
4226
|
if (head.length < stamp.size && head.at(-1) !== 10) {
|
|
4484
4227
|
const chunks = [head];
|
|
@@ -4501,42 +4244,51 @@ function sample(path, until) {
|
|
|
4501
4244
|
);
|
|
4502
4245
|
if (start === head.length || tail.some(until)) break;
|
|
4503
4246
|
}
|
|
4504
|
-
const
|
|
4505
|
-
metadata.set(path, { stamp,
|
|
4506
|
-
return
|
|
4247
|
+
const value2 = describe2([...records(head), ...tail], stamp);
|
|
4248
|
+
metadata.set(path, { stamp, value: value2 });
|
|
4249
|
+
return value2;
|
|
4507
4250
|
}
|
|
4508
|
-
|
|
4251
|
+
var windowed = (path, messages5) => journals.get(path)?.start ? [EARLIER, ...messages5] : messages5;
|
|
4252
|
+
function journal(path, latest = false) {
|
|
4509
4253
|
const stamp = statSync(path);
|
|
4510
4254
|
let saved = journals.get(path);
|
|
4511
4255
|
if (saved && unchanged(saved.stamp, stamp)) {
|
|
4256
|
+
journals.delete(path);
|
|
4257
|
+
journals.set(path, saved);
|
|
4512
4258
|
const tail2 = parse(saved.tail.toString("utf8"));
|
|
4513
4259
|
return tail2 ? [...saved.rows, tail2] : saved.rows;
|
|
4514
4260
|
}
|
|
4515
|
-
if (!saved || saved.stamp.ino !== stamp.ino || stamp.size <= saved.offset)
|
|
4516
|
-
|
|
4261
|
+
if (!saved || saved.stamp.ino !== stamp.ino || stamp.size <= saved.offset || latest && stamp.size - saved.start > 2 * LATEST_BYTES) {
|
|
4262
|
+
const start = latest ? Math.max(0, stamp.size - LATEST_BYTES) : 0;
|
|
4263
|
+
saved = { stamp, start, offset: start, tail: Buffer.alloc(0), rows: [] };
|
|
4264
|
+
}
|
|
4517
4265
|
while (saved.offset < stamp.size) {
|
|
4518
4266
|
const chunk = readRange(path, saved.offset, Math.min(READ_BYTES, stamp.size - saved.offset));
|
|
4519
4267
|
if (!chunk.length) break;
|
|
4268
|
+
const first = saved.offset === saved.start && saved.start > 0;
|
|
4520
4269
|
saved.offset += chunk.length;
|
|
4521
4270
|
const buffer = Buffer.concat([saved.tail, chunk]);
|
|
4522
4271
|
const end = buffer.lastIndexOf(10);
|
|
4523
4272
|
if (end < 0) {
|
|
4524
|
-
saved.tail = buffer;
|
|
4273
|
+
saved.tail = first ? Buffer.alloc(0) : buffer;
|
|
4525
4274
|
continue;
|
|
4526
4275
|
}
|
|
4527
|
-
|
|
4276
|
+
const lines = buffer.subarray(0, end).toString("utf8").split("\n");
|
|
4277
|
+
for (const line of first ? lines.slice(1) : lines) {
|
|
4528
4278
|
const row = parse(line);
|
|
4529
4279
|
if (row) saved.rows.push(row);
|
|
4530
4280
|
}
|
|
4531
4281
|
saved.tail = Buffer.from(buffer.subarray(end + 1));
|
|
4532
4282
|
}
|
|
4533
4283
|
saved.stamp = stamp;
|
|
4284
|
+
journals.delete(path);
|
|
4534
4285
|
journals.set(path, saved);
|
|
4286
|
+
if (journals.size > JOURNALS) journals.delete(journals.keys().next().value);
|
|
4535
4287
|
const tail = parse(saved.tail.toString("utf8"));
|
|
4536
4288
|
return tail ? [...saved.rows, tail] : saved.rows;
|
|
4537
4289
|
}
|
|
4538
4290
|
function files(root, scan, depth = Infinity) {
|
|
4539
|
-
if (!
|
|
4291
|
+
if (!existsSync2(root)) {
|
|
4540
4292
|
watchParent(root, scan);
|
|
4541
4293
|
return [];
|
|
4542
4294
|
}
|
|
@@ -4553,8 +4305,8 @@ function files(root, scan, depth = Infinity) {
|
|
|
4553
4305
|
}
|
|
4554
4306
|
}
|
|
4555
4307
|
function watchParent(path, scan) {
|
|
4556
|
-
while (!
|
|
4557
|
-
if (
|
|
4308
|
+
while (!existsSync2(path) && dirname2(path) !== path) path = dirname2(path);
|
|
4309
|
+
if (existsSync2(path)) scan.paths.add(path);
|
|
4558
4310
|
}
|
|
4559
4311
|
function canonical(cwd) {
|
|
4560
4312
|
if (typeof cwd !== "string" || !isAbsolute(cwd)) return void 0;
|
|
@@ -4659,7 +4411,7 @@ function claudeChange(change2) {
|
|
|
4659
4411
|
].join("\n");
|
|
4660
4412
|
}
|
|
4661
4413
|
function claudeMessages(path) {
|
|
4662
|
-
const rows = journal(path);
|
|
4414
|
+
const rows = journal(path, true);
|
|
4663
4415
|
const chain = /* @__PURE__ */ new Map();
|
|
4664
4416
|
let leaf;
|
|
4665
4417
|
for (const row of rows) {
|
|
@@ -4679,6 +4431,9 @@ function claudeMessages(path) {
|
|
|
4679
4431
|
leaf = typeof row.parentUuid === "string" ? row.parentUuid : void 0;
|
|
4680
4432
|
}
|
|
4681
4433
|
const conversation = selected.length ? selected.reverse() : rows;
|
|
4434
|
+
return windowed(path, claudeConversation(conversation, rows));
|
|
4435
|
+
}
|
|
4436
|
+
function claudeConversation(conversation, rows) {
|
|
4682
4437
|
const results = /* @__PURE__ */ new Map();
|
|
4683
4438
|
for (const row of rows)
|
|
4684
4439
|
for (const part of row.type === "user" ? list(row.message?.content) : [])
|
|
@@ -4712,26 +4467,31 @@ function claudeMessages(path) {
|
|
|
4712
4467
|
function claude(root, scan) {
|
|
4713
4468
|
for (const path of files(join2(root, "projects"), scan, 1).filter((file) => file.endsWith(".jsonl"))) {
|
|
4714
4469
|
try {
|
|
4715
|
-
const
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4470
|
+
const session = described(path, claudeMessage, (rows, stamp) => {
|
|
4471
|
+
const latest = [...rows].reverse();
|
|
4472
|
+
const owner = rows.find((row) => typeof row.sessionId === "string" && canonical(row.cwd));
|
|
4473
|
+
if (!owner) return void 0;
|
|
4474
|
+
const named = latest.find((row) => row.type === "custom-title" || row.type === "ai-title");
|
|
4475
|
+
const first = rows.find(
|
|
4476
|
+
(row) => row.type === "user" && !row.isMeta && !row.isSidechain && !injected(row.message?.content)
|
|
4477
|
+
);
|
|
4478
|
+
return {
|
|
4479
|
+
id: String(owner.sessionId),
|
|
4480
|
+
cwd: canonical(owner.cwd),
|
|
4481
|
+
title: title(
|
|
4482
|
+
named?.customTitle ?? named?.aiTitle ?? text(first?.message?.content),
|
|
4483
|
+
"Claude Code thread"
|
|
4484
|
+
),
|
|
4485
|
+
updatedAt: date(latest.find(claudeMessage)?.timestamp, stamp.mtimeMs)
|
|
4486
|
+
};
|
|
4487
|
+
});
|
|
4488
|
+
if (!session) {
|
|
4719
4489
|
scan.warnings.push(`Unrecognized Claude Code session: ${path}`);
|
|
4720
4490
|
continue;
|
|
4721
4491
|
}
|
|
4722
|
-
const named = latest.find((row) => row.type === "custom-title" || row.type === "ai-title");
|
|
4723
|
-
const first = rows.find(
|
|
4724
|
-
(row) => row.type === "user" && !row.isMeta && !row.isSidechain && !injected(row.message?.content)
|
|
4725
|
-
);
|
|
4726
4492
|
scan.sessions.push({
|
|
4727
4493
|
harness: "claude",
|
|
4728
|
-
|
|
4729
|
-
cwd: canonical(owner.cwd),
|
|
4730
|
-
title: title(
|
|
4731
|
-
named?.customTitle ?? named?.aiTitle ?? text(first?.message?.content),
|
|
4732
|
-
"Claude Code thread"
|
|
4733
|
-
),
|
|
4734
|
-
updatedAt: date(latest.find(claudeMessage)?.timestamp, statSync(path).mtimeMs),
|
|
4494
|
+
...session,
|
|
4735
4495
|
files: [path],
|
|
4736
4496
|
read: () => claudeMessages(path)
|
|
4737
4497
|
});
|
|
@@ -4759,7 +4519,7 @@ function applyPatch(patch) {
|
|
|
4759
4519
|
).replace(/^\*\*\* .*\n?/gm, "");
|
|
4760
4520
|
}
|
|
4761
4521
|
function codexMessages(path) {
|
|
4762
|
-
const rows = journal(path);
|
|
4522
|
+
const rows = journal(path, true);
|
|
4763
4523
|
const outputs = /* @__PURE__ */ new Map();
|
|
4764
4524
|
for (const row of rows)
|
|
4765
4525
|
if (["function_call_output", "custom_tool_call_output"].includes(row.payload?.type))
|
|
@@ -4788,8 +4548,8 @@ function codexMessages(path) {
|
|
|
4788
4548
|
)
|
|
4789
4549
|
];
|
|
4790
4550
|
});
|
|
4791
|
-
if (result.length) return result;
|
|
4792
|
-
return rows.flatMap((row, index) => {
|
|
4551
|
+
if (result.length) return windowed(path, result);
|
|
4552
|
+
return windowed(path, rows.flatMap((row, index) => {
|
|
4793
4553
|
if (row.type !== "event_msg") return [];
|
|
4794
4554
|
const item = object(row.payload);
|
|
4795
4555
|
if (!item || !["user_message", "agent_message"].includes(item.type)) return [];
|
|
@@ -4798,12 +4558,12 @@ function codexMessages(path) {
|
|
|
4798
4558
|
item.type === "user_message" ? "user" : "assistant",
|
|
4799
4559
|
item.message
|
|
4800
4560
|
);
|
|
4801
|
-
});
|
|
4561
|
+
}));
|
|
4802
4562
|
}
|
|
4803
4563
|
function codex(root, scan) {
|
|
4804
4564
|
const index = join2(root, "session_index.jsonl");
|
|
4805
4565
|
const names = /* @__PURE__ */ new Map();
|
|
4806
|
-
if (
|
|
4566
|
+
if (existsSync2(index)) {
|
|
4807
4567
|
scan.paths.add(root);
|
|
4808
4568
|
try {
|
|
4809
4569
|
for (const row of journal(index)) if (typeof row.id === "string") names.set(row.id, row);
|
|
@@ -4812,34 +4572,41 @@ function codex(root, scan) {
|
|
|
4812
4572
|
}
|
|
4813
4573
|
}
|
|
4814
4574
|
const titles = codexTitles(root);
|
|
4575
|
+
const indexed = existsSync2(index);
|
|
4815
4576
|
for (const path of [
|
|
4816
4577
|
...files(join2(root, "sessions"), scan),
|
|
4817
4578
|
...files(join2(root, "archived_sessions"), scan)
|
|
4818
4579
|
].filter((file) => file.endsWith(".jsonl"))) {
|
|
4819
4580
|
try {
|
|
4820
|
-
const
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4581
|
+
const session = described(path, codexItem, (rows, stamp) => {
|
|
4582
|
+
const owner = rows.find((row) => row.type === "session_meta")?.payload;
|
|
4583
|
+
const cwd = canonical(owner?.cwd);
|
|
4584
|
+
if (typeof owner?.id !== "string" || !cwd) return void 0;
|
|
4585
|
+
const first = rows.find(
|
|
4586
|
+
(row) => row.type === "response_item" && row.payload?.type === "message" && row.payload.role === "user" && !injected(row.payload.content)
|
|
4587
|
+
);
|
|
4588
|
+
const parent = owner.parent_thread_id;
|
|
4589
|
+
return {
|
|
4590
|
+
id: owner.id,
|
|
4591
|
+
cwd,
|
|
4592
|
+
asked: text(first?.payload?.content),
|
|
4593
|
+
parent: typeof parent === "string" ? parent : void 0,
|
|
4594
|
+
updatedAt: date([...rows].reverse().find(codexItem)?.timestamp, stamp.mtimeMs)
|
|
4595
|
+
};
|
|
4596
|
+
});
|
|
4597
|
+
if (!session) {
|
|
4825
4598
|
scan.warnings.push(`Unrecognized Codex session: ${path}`);
|
|
4826
4599
|
continue;
|
|
4827
4600
|
}
|
|
4828
|
-
const
|
|
4829
|
-
const first = rows.find(
|
|
4830
|
-
(row) => row.type === "response_item" && row.payload?.type === "message" && row.payload.role === "user" && !injected(row.payload.content)
|
|
4831
|
-
);
|
|
4601
|
+
const { asked, ...found } = session;
|
|
4832
4602
|
scan.sessions.push({
|
|
4833
4603
|
harness: "codex",
|
|
4834
|
-
|
|
4835
|
-
cwd,
|
|
4604
|
+
...found,
|
|
4836
4605
|
title: title(
|
|
4837
|
-
|
|
4606
|
+
names.get(found.id)?.thread_name ?? titles.get(found.id) ?? asked,
|
|
4838
4607
|
"Codex thread"
|
|
4839
4608
|
),
|
|
4840
|
-
|
|
4841
|
-
updatedAt: date(latest.find(codexItem)?.timestamp, statSync(path).mtimeMs),
|
|
4842
|
-
files: [path, ...existsSync3(index) ? [index] : []],
|
|
4609
|
+
files: [path, ...indexed ? [index] : []],
|
|
4843
4610
|
read: () => codexMessages(path)
|
|
4844
4611
|
});
|
|
4845
4612
|
} catch {
|
|
@@ -4906,17 +4673,22 @@ function databaseMessages(path, session) {
|
|
|
4906
4673
|
}
|
|
4907
4674
|
}
|
|
4908
4675
|
function codexSubagent(row) {
|
|
4909
|
-
const
|
|
4910
|
-
const task = typeof
|
|
4676
|
+
const spawn6 = object(object(parse(String(row.source ?? "")))?.subagent)?.thread_spawn;
|
|
4677
|
+
const task = typeof spawn6?.agent_path === "string" ? spawn6.agent_path.split("/").pop() : "";
|
|
4911
4678
|
return [row.agent_nickname, task?.replace(/_/g, " ")].filter(Boolean).join(" \xB7 ");
|
|
4912
4679
|
}
|
|
4680
|
+
var codexState;
|
|
4913
4681
|
function codexTitles(root) {
|
|
4914
4682
|
const titles = /* @__PURE__ */ new Map();
|
|
4915
|
-
const state = (
|
|
4683
|
+
const state = (existsSync2(root) ? readdirSync(root) : []).filter((name) => /^state_\d+\.sqlite$/.test(name)).sort((a, b) => Number(b.match(/\d+/)[0]) - Number(a.match(/\d+/)[0]))[0];
|
|
4916
4684
|
if (!state) return titles;
|
|
4685
|
+
const path = join2(root, state);
|
|
4686
|
+
const stamp = [path, `${path}-wal`].map((file) => statSync(file, { throwIfNoEntry: false })).map((found) => found ? `${found.ino}:${found.size}:${found.mtimeMs}` : "").join("|");
|
|
4687
|
+
if (codexState?.stamp === stamp) return codexState.titles;
|
|
4688
|
+
codexState = { stamp, titles };
|
|
4917
4689
|
let db;
|
|
4918
4690
|
try {
|
|
4919
|
-
db = openDatabase(
|
|
4691
|
+
db = openDatabase(path);
|
|
4920
4692
|
for (const row of db.prepare("SELECT id, title, first_user_message, source, agent_nickname FROM threads").all()) {
|
|
4921
4693
|
const name = [row.title, row.first_user_message].find(
|
|
4922
4694
|
(value2) => typeof value2 === "string" && !!value2.trim() && !injected(value2)
|
|
@@ -4930,7 +4702,7 @@ function codexTitles(root) {
|
|
|
4930
4702
|
return titles;
|
|
4931
4703
|
}
|
|
4932
4704
|
function openCodeDatabase(path, scan) {
|
|
4933
|
-
if (!
|
|
4705
|
+
if (!existsSync2(path)) {
|
|
4934
4706
|
watchParent(path, scan);
|
|
4935
4707
|
return;
|
|
4936
4708
|
}
|
|
@@ -4992,7 +4764,7 @@ function legacyMessages(root, session) {
|
|
|
4992
4764
|
}
|
|
4993
4765
|
function openCodeLegacy(root, scan) {
|
|
4994
4766
|
const parts2 = join2(root, "part");
|
|
4995
|
-
if (
|
|
4767
|
+
if (existsSync2(parts2)) files(parts2, scan, 1);
|
|
4996
4768
|
for (const path of files(join2(root, "session"), scan).filter((file) => file.endsWith(".json"))) {
|
|
4997
4769
|
try {
|
|
4998
4770
|
const row = jsonFile(path);
|
|
@@ -5045,7 +4817,22 @@ function scanHistories() {
|
|
|
5045
4817
|
}
|
|
5046
4818
|
|
|
5047
4819
|
// src/discovery.ts
|
|
5048
|
-
var
|
|
4820
|
+
var READ_ONLY = {
|
|
4821
|
+
read: true,
|
|
4822
|
+
send: false,
|
|
4823
|
+
queue: false,
|
|
4824
|
+
stop: false,
|
|
4825
|
+
approve: false
|
|
4826
|
+
};
|
|
4827
|
+
var opaqueIds = /* @__PURE__ */ new Map();
|
|
4828
|
+
var opaqueId = (value2, secret) => {
|
|
4829
|
+
const key = `${secret}\0${value2}`;
|
|
4830
|
+
const known = opaqueIds.get(key);
|
|
4831
|
+
if (known) return known;
|
|
4832
|
+
const id = createHmac("sha256", secret).update(value2).digest("hex").slice(0, 32);
|
|
4833
|
+
opaqueIds.set(key, id);
|
|
4834
|
+
return id;
|
|
4835
|
+
};
|
|
5049
4836
|
var agentThread = (harness, session, secret) => opaqueId(`agent\0${harness}\0${session}`, secret);
|
|
5050
4837
|
var zedHost = (value2) => typeof value2 === "string" ? value2 : value2 && typeof value2 === "object" ? String(Object.values(value2)[0] ?? "") : "";
|
|
5051
4838
|
function vscodeHost(authority) {
|
|
@@ -5131,21 +4918,23 @@ function contentText(content) {
|
|
|
5131
4918
|
return typeof part.text === "string" ? part.text : typeof part.Text === "string" ? part.Text : typeof part.value === "string" ? part.value : contentText(part.content);
|
|
5132
4919
|
}
|
|
5133
4920
|
var Discovery = class {
|
|
5134
|
-
constructor(deviceId,
|
|
4921
|
+
constructor(deviceId, secret) {
|
|
5135
4922
|
this.deviceId = deviceId;
|
|
5136
|
-
this.capabilities = capabilities;
|
|
5137
4923
|
this.secret = secret;
|
|
5138
4924
|
}
|
|
5139
4925
|
deviceId;
|
|
5140
|
-
capabilities;
|
|
5141
4926
|
secret;
|
|
5142
4927
|
projects = /* @__PURE__ */ new Map();
|
|
5143
4928
|
locations = /* @__PURE__ */ new Map();
|
|
4929
|
+
// Each native agent session's thread, so finding it costs the same however many sessions a scan
|
|
4930
|
+
// sees. The first project to list a thread keeps it.
|
|
4931
|
+
agents = /* @__PURE__ */ new Map();
|
|
5144
4932
|
paths = /* @__PURE__ */ new Set();
|
|
5145
4933
|
warnings = [];
|
|
5146
4934
|
scan() {
|
|
5147
4935
|
this.projects.clear();
|
|
5148
4936
|
this.locations.clear();
|
|
4937
|
+
this.agents.clear();
|
|
5149
4938
|
this.paths.clear();
|
|
5150
4939
|
this.warnings.length = 0;
|
|
5151
4940
|
const defaults = dataPaths();
|
|
@@ -5160,12 +4949,8 @@ var Discovery = class {
|
|
|
5160
4949
|
return [...this.projects.values()];
|
|
5161
4950
|
}
|
|
5162
4951
|
native(session) {
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
(item) => item.agent?.harness === session.harness && item.agent.sessionId === session.id
|
|
5166
|
-
);
|
|
5167
|
-
if (thread2) return { project: project2, thread: thread2 };
|
|
5168
|
-
}
|
|
4952
|
+
const known = this.agents.get(`${session.harness}\0${session.id}`);
|
|
4953
|
+
if (known) return known;
|
|
5169
4954
|
const project = this.project(session.cwd);
|
|
5170
4955
|
const thread = {
|
|
5171
4956
|
id: agentThread(session.harness, session.id, this.secret),
|
|
@@ -5173,11 +4958,12 @@ var Discovery = class {
|
|
|
5173
4958
|
editor: "cli",
|
|
5174
4959
|
status: "unknown",
|
|
5175
4960
|
updatedAt: session.updatedAt,
|
|
5176
|
-
capabilities: { ...
|
|
4961
|
+
capabilities: { ...READ_ONLY },
|
|
5177
4962
|
agent: { harness: session.harness, sessionId: session.id }
|
|
5178
4963
|
};
|
|
5179
4964
|
project.threads.push(thread);
|
|
5180
4965
|
this.locations.set(thread.id, { editor: "cli", path: "" });
|
|
4966
|
+
this.agents.set(`${session.harness}\0${session.id}`, { project, thread });
|
|
5181
4967
|
return { project, thread };
|
|
5182
4968
|
}
|
|
5183
4969
|
// The native history owns the conversation's last message time, whichever editor lists it.
|
|
@@ -5196,7 +4982,7 @@ var Discovery = class {
|
|
|
5196
4982
|
const parent = dirname3(project.path);
|
|
5197
4983
|
counts.set(parent, (counts.get(parent) ?? 0) + 1);
|
|
5198
4984
|
}
|
|
5199
|
-
const folders = [...counts].sort((a, b) => b[1] - a[1]).map(([folder]) => folder).filter((folder) =>
|
|
4985
|
+
const folders = [...counts].sort((a, b) => b[1] - a[1]).map(([folder]) => folder).filter((folder) => existsSync3(folder)).slice(0, 3);
|
|
5200
4986
|
return folders.length ? folders : [homedir3()];
|
|
5201
4987
|
}
|
|
5202
4988
|
observe(session) {
|
|
@@ -5233,7 +5019,7 @@ var Discovery = class {
|
|
|
5233
5019
|
editor: "acp",
|
|
5234
5020
|
status: "idle",
|
|
5235
5021
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5236
|
-
capabilities:
|
|
5022
|
+
capabilities: READ_ONLY
|
|
5237
5023
|
};
|
|
5238
5024
|
project.threads.push(thread);
|
|
5239
5025
|
this.locations.set(id, { editor: "acp", path: "", session });
|
|
@@ -5261,11 +5047,11 @@ var Discovery = class {
|
|
|
5261
5047
|
zed(root) {
|
|
5262
5048
|
const database = join3(root, "db");
|
|
5263
5049
|
const store = join3(root, "threads/threads.db");
|
|
5264
|
-
if (!
|
|
5050
|
+
if (!existsSync3(database)) return;
|
|
5265
5051
|
this.paths.add(database);
|
|
5266
5052
|
for (const entry of readdirSync2(database)) {
|
|
5267
5053
|
const path = join3(database, entry, "db.sqlite");
|
|
5268
|
-
if (!
|
|
5054
|
+
if (!existsSync3(path)) continue;
|
|
5269
5055
|
this.paths.add(join3(database, entry));
|
|
5270
5056
|
try {
|
|
5271
5057
|
if (!sqlite(
|
|
@@ -5274,7 +5060,7 @@ var Discovery = class {
|
|
|
5274
5060
|
).length)
|
|
5275
5061
|
continue;
|
|
5276
5062
|
const stamps = new Map(
|
|
5277
|
-
|
|
5063
|
+
existsSync3(store) ? sqlite(store, "SELECT id, updated_at FROM threads").map((row) => [
|
|
5278
5064
|
row.id,
|
|
5279
5065
|
row.updated_at
|
|
5280
5066
|
]) : []
|
|
@@ -5300,16 +5086,20 @@ var Discovery = class {
|
|
|
5300
5086
|
...ssh.port ? { port: Number(ssh.port) } : {}
|
|
5301
5087
|
} : void 0;
|
|
5302
5088
|
const project = this.project(folder, remote);
|
|
5303
|
-
if (!project.threads.some((thread) => thread.id === id))
|
|
5304
|
-
|
|
5089
|
+
if (!project.threads.some((thread) => thread.id === id)) {
|
|
5090
|
+
const thread = {
|
|
5305
5091
|
id,
|
|
5306
5092
|
title: row.title_override || row.title,
|
|
5307
5093
|
editor: "zed",
|
|
5308
5094
|
status: "unknown",
|
|
5309
5095
|
updatedAt,
|
|
5310
|
-
capabilities:
|
|
5096
|
+
capabilities: READ_ONLY,
|
|
5311
5097
|
...harness ? { agent: { harness, sessionId: session } } : {}
|
|
5312
|
-
}
|
|
5098
|
+
};
|
|
5099
|
+
project.threads.push(thread);
|
|
5100
|
+
if (harness && !this.agents.has(`${harness}\0${session}`))
|
|
5101
|
+
this.agents.set(`${harness}\0${session}`, { project, thread });
|
|
5102
|
+
}
|
|
5313
5103
|
this.locations.set(id, { editor: "zed", path: store, session });
|
|
5314
5104
|
}
|
|
5315
5105
|
}
|
|
@@ -5318,17 +5108,17 @@ var Discovery = class {
|
|
|
5318
5108
|
}
|
|
5319
5109
|
}
|
|
5320
5110
|
const threads = join3(root, "threads");
|
|
5321
|
-
if (
|
|
5111
|
+
if (existsSync3(threads)) this.paths.add(threads);
|
|
5322
5112
|
}
|
|
5323
5113
|
vscode(root) {
|
|
5324
5114
|
const workspaces = join3(root, "workspaceStorage");
|
|
5325
|
-
if (!
|
|
5115
|
+
if (!existsSync3(workspaces)) return;
|
|
5326
5116
|
this.paths.add(workspaces);
|
|
5327
5117
|
for (const entry of readdirSync2(workspaces)) {
|
|
5328
5118
|
const directory2 = join3(workspaces, entry);
|
|
5329
5119
|
const workspace = join3(directory2, "workspace.json");
|
|
5330
5120
|
const sessions = join3(directory2, "chatSessions");
|
|
5331
|
-
if (!
|
|
5121
|
+
if (!existsSync3(workspace)) continue;
|
|
5332
5122
|
this.paths.add(directory2);
|
|
5333
5123
|
try {
|
|
5334
5124
|
const data = JSON.parse(readFileSync2(workspace, "utf8"));
|
|
@@ -5337,7 +5127,7 @@ var Discovery = class {
|
|
|
5337
5127
|
const path = decodeURIComponent(uri.pathname).replace(/^\/([A-Za-z]:\/)/, "$1");
|
|
5338
5128
|
const host = uri.protocol === "vscode-remote:" ? vscodeHost(uri.host) : "";
|
|
5339
5129
|
const project = this.project(path, host ? { host, path } : void 0);
|
|
5340
|
-
if (!
|
|
5130
|
+
if (!existsSync3(sessions)) continue;
|
|
5341
5131
|
this.paths.add(sessions);
|
|
5342
5132
|
for (const filename of readdirSync2(sessions).filter((name) => /\.jsonl?$/.test(name))) {
|
|
5343
5133
|
const file = join3(sessions, filename);
|
|
@@ -5349,7 +5139,7 @@ var Discovery = class {
|
|
|
5349
5139
|
editor: "vscode",
|
|
5350
5140
|
status: "unknown",
|
|
5351
5141
|
updatedAt: new Date(state.lastMessageDate ?? state.creationDate ?? 0).toISOString(),
|
|
5352
|
-
capabilities:
|
|
5142
|
+
capabilities: READ_ONLY
|
|
5353
5143
|
});
|
|
5354
5144
|
this.locations.set(id, { editor: "vscode", path: file });
|
|
5355
5145
|
}
|
|
@@ -5375,16 +5165,16 @@ var Discovery = class {
|
|
|
5375
5165
|
}
|
|
5376
5166
|
]).filter((message) => message.text);
|
|
5377
5167
|
}
|
|
5378
|
-
if (!
|
|
5168
|
+
if (!existsSync3(location2.path))
|
|
5379
5169
|
throw new Error(
|
|
5380
|
-
"This external agent keeps its conversation in its own storage.
|
|
5170
|
+
"This external agent keeps its conversation in its own storage. View the open thread on your computer."
|
|
5381
5171
|
);
|
|
5382
5172
|
const db = new DatabaseSync2(location2.path, { readOnly: true });
|
|
5383
5173
|
try {
|
|
5384
5174
|
const row = db.prepare("SELECT data_type, data FROM threads WHERE id = ?").get(location2.session);
|
|
5385
5175
|
if (!row)
|
|
5386
5176
|
throw new Error(
|
|
5387
|
-
"This agent history is not available on this computer.
|
|
5177
|
+
"This agent history is not available on this computer. View the existing session in Zed."
|
|
5388
5178
|
);
|
|
5389
5179
|
const data = row.data_type === "zstd" ? zstdDecompressSync(row.data) : row.data;
|
|
5390
5180
|
const state = JSON.parse(Buffer.from(data).toString("utf8"));
|
|
@@ -10612,20 +10402,20 @@ var deflate$2 = (strm, flush) => {
|
|
|
10612
10402
|
let beg = s.pending;
|
|
10613
10403
|
let left = (s.gzhead.extra.length & 65535) - s.gzindex;
|
|
10614
10404
|
while (s.pending + left > s.pending_buf_size) {
|
|
10615
|
-
let
|
|
10616
|
-
s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex +
|
|
10405
|
+
let copy2 = s.pending_buf_size - s.pending;
|
|
10406
|
+
s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + copy2), s.pending);
|
|
10617
10407
|
s.pending = s.pending_buf_size;
|
|
10618
10408
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
10619
10409
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
10620
10410
|
}
|
|
10621
|
-
s.gzindex +=
|
|
10411
|
+
s.gzindex += copy2;
|
|
10622
10412
|
flush_pending(strm);
|
|
10623
10413
|
if (s.pending !== 0) {
|
|
10624
10414
|
s.last_flush = -1;
|
|
10625
10415
|
return Z_OK$3;
|
|
10626
10416
|
}
|
|
10627
10417
|
beg = 0;
|
|
10628
|
-
left -=
|
|
10418
|
+
left -= copy2;
|
|
10629
10419
|
}
|
|
10630
10420
|
let gzhead_extra = new Uint8Array(s.gzhead.extra);
|
|
10631
10421
|
s.pending_buf.set(gzhead_extra.subarray(s.gzindex, s.gzindex + left), s.pending);
|
|
@@ -11935,7 +11725,7 @@ var fixedtables = (state) => {
|
|
|
11935
11725
|
state.distcode = distfix;
|
|
11936
11726
|
state.distbits = 5;
|
|
11937
11727
|
};
|
|
11938
|
-
var updatewindow = (strm, src, end,
|
|
11728
|
+
var updatewindow = (strm, src, end, copy2) => {
|
|
11939
11729
|
let dist;
|
|
11940
11730
|
const state = strm.state;
|
|
11941
11731
|
if (state.window === null) {
|
|
@@ -11946,20 +11736,20 @@ var updatewindow = (strm, src, end, copy) => {
|
|
|
11946
11736
|
state.wnext = 0;
|
|
11947
11737
|
state.whave = 0;
|
|
11948
11738
|
}
|
|
11949
|
-
if (
|
|
11739
|
+
if (copy2 >= state.wsize) {
|
|
11950
11740
|
state.window.set(src.subarray(end - state.wsize, end), 0);
|
|
11951
11741
|
state.wnext = 0;
|
|
11952
11742
|
state.whave = state.wsize;
|
|
11953
11743
|
} else {
|
|
11954
11744
|
dist = state.wsize - state.wnext;
|
|
11955
|
-
if (dist >
|
|
11956
|
-
dist =
|
|
11957
|
-
}
|
|
11958
|
-
state.window.set(src.subarray(end -
|
|
11959
|
-
|
|
11960
|
-
if (
|
|
11961
|
-
state.window.set(src.subarray(end -
|
|
11962
|
-
state.wnext =
|
|
11745
|
+
if (dist > copy2) {
|
|
11746
|
+
dist = copy2;
|
|
11747
|
+
}
|
|
11748
|
+
state.window.set(src.subarray(end - copy2, end - copy2 + dist), state.wnext);
|
|
11749
|
+
copy2 -= dist;
|
|
11750
|
+
if (copy2) {
|
|
11751
|
+
state.window.set(src.subarray(end - copy2, end), 0);
|
|
11752
|
+
state.wnext = copy2;
|
|
11963
11753
|
state.whave = state.wsize;
|
|
11964
11754
|
} else {
|
|
11965
11755
|
state.wnext += dist;
|
|
@@ -11982,7 +11772,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
11982
11772
|
let hold;
|
|
11983
11773
|
let bits;
|
|
11984
11774
|
let _in, _out;
|
|
11985
|
-
let
|
|
11775
|
+
let copy2;
|
|
11986
11776
|
let from;
|
|
11987
11777
|
let from_source;
|
|
11988
11778
|
let here = 0;
|
|
@@ -12181,11 +11971,11 @@ var inflate$2 = (strm, flush) => {
|
|
|
12181
11971
|
/* falls through */
|
|
12182
11972
|
case EXTRA:
|
|
12183
11973
|
if (state.flags & 1024) {
|
|
12184
|
-
|
|
12185
|
-
if (
|
|
12186
|
-
|
|
11974
|
+
copy2 = state.length;
|
|
11975
|
+
if (copy2 > have) {
|
|
11976
|
+
copy2 = have;
|
|
12187
11977
|
}
|
|
12188
|
-
if (
|
|
11978
|
+
if (copy2) {
|
|
12189
11979
|
if (state.head) {
|
|
12190
11980
|
len = state.head.extra_len - state.length;
|
|
12191
11981
|
if (!state.head.extra) {
|
|
@@ -12196,18 +11986,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12196
11986
|
next,
|
|
12197
11987
|
// extra field is limited to 65536 bytes
|
|
12198
11988
|
// - no need for additional size check
|
|
12199
|
-
next +
|
|
11989
|
+
next + copy2
|
|
12200
11990
|
),
|
|
12201
11991
|
/*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
|
|
12202
11992
|
len
|
|
12203
11993
|
);
|
|
12204
11994
|
}
|
|
12205
11995
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12206
|
-
state.check = crc32_1(state.check, input,
|
|
11996
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12207
11997
|
}
|
|
12208
|
-
have -=
|
|
12209
|
-
next +=
|
|
12210
|
-
state.length -=
|
|
11998
|
+
have -= copy2;
|
|
11999
|
+
next += copy2;
|
|
12000
|
+
state.length -= copy2;
|
|
12211
12001
|
}
|
|
12212
12002
|
if (state.length) {
|
|
12213
12003
|
break inf_leave;
|
|
@@ -12221,18 +12011,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12221
12011
|
if (have === 0) {
|
|
12222
12012
|
break inf_leave;
|
|
12223
12013
|
}
|
|
12224
|
-
|
|
12014
|
+
copy2 = 0;
|
|
12225
12015
|
do {
|
|
12226
|
-
len = input[next +
|
|
12016
|
+
len = input[next + copy2++];
|
|
12227
12017
|
if (state.head && len && state.length < 65536) {
|
|
12228
12018
|
state.head.name += String.fromCharCode(len);
|
|
12229
12019
|
}
|
|
12230
|
-
} while (len &&
|
|
12020
|
+
} while (len && copy2 < have);
|
|
12231
12021
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12232
|
-
state.check = crc32_1(state.check, input,
|
|
12022
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12233
12023
|
}
|
|
12234
|
-
have -=
|
|
12235
|
-
next +=
|
|
12024
|
+
have -= copy2;
|
|
12025
|
+
next += copy2;
|
|
12236
12026
|
if (len) {
|
|
12237
12027
|
break inf_leave;
|
|
12238
12028
|
}
|
|
@@ -12247,18 +12037,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12247
12037
|
if (have === 0) {
|
|
12248
12038
|
break inf_leave;
|
|
12249
12039
|
}
|
|
12250
|
-
|
|
12040
|
+
copy2 = 0;
|
|
12251
12041
|
do {
|
|
12252
|
-
len = input[next +
|
|
12042
|
+
len = input[next + copy2++];
|
|
12253
12043
|
if (state.head && len && state.length < 65536) {
|
|
12254
12044
|
state.head.comment += String.fromCharCode(len);
|
|
12255
12045
|
}
|
|
12256
|
-
} while (len &&
|
|
12046
|
+
} while (len && copy2 < have);
|
|
12257
12047
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12258
|
-
state.check = crc32_1(state.check, input,
|
|
12048
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12259
12049
|
}
|
|
12260
|
-
have -=
|
|
12261
|
-
next +=
|
|
12050
|
+
have -= copy2;
|
|
12051
|
+
next += copy2;
|
|
12262
12052
|
if (len) {
|
|
12263
12053
|
break inf_leave;
|
|
12264
12054
|
}
|
|
@@ -12393,23 +12183,23 @@ var inflate$2 = (strm, flush) => {
|
|
|
12393
12183
|
state.mode = COPY;
|
|
12394
12184
|
/* falls through */
|
|
12395
12185
|
case COPY:
|
|
12396
|
-
|
|
12397
|
-
if (
|
|
12398
|
-
if (
|
|
12399
|
-
|
|
12186
|
+
copy2 = state.length;
|
|
12187
|
+
if (copy2) {
|
|
12188
|
+
if (copy2 > have) {
|
|
12189
|
+
copy2 = have;
|
|
12400
12190
|
}
|
|
12401
|
-
if (
|
|
12402
|
-
|
|
12191
|
+
if (copy2 > left) {
|
|
12192
|
+
copy2 = left;
|
|
12403
12193
|
}
|
|
12404
|
-
if (
|
|
12194
|
+
if (copy2 === 0) {
|
|
12405
12195
|
break inf_leave;
|
|
12406
12196
|
}
|
|
12407
|
-
output.set(input.subarray(next, next +
|
|
12408
|
-
have -=
|
|
12409
|
-
next +=
|
|
12410
|
-
left -=
|
|
12411
|
-
put +=
|
|
12412
|
-
state.length -=
|
|
12197
|
+
output.set(input.subarray(next, next + copy2), put);
|
|
12198
|
+
have -= copy2;
|
|
12199
|
+
next += copy2;
|
|
12200
|
+
left -= copy2;
|
|
12201
|
+
put += copy2;
|
|
12202
|
+
state.length -= copy2;
|
|
12413
12203
|
break;
|
|
12414
12204
|
}
|
|
12415
12205
|
state.mode = TYPE;
|
|
@@ -12510,7 +12300,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
12510
12300
|
break;
|
|
12511
12301
|
}
|
|
12512
12302
|
len = state.lens[state.have - 1];
|
|
12513
|
-
|
|
12303
|
+
copy2 = 3 + (hold & 3);
|
|
12514
12304
|
hold >>>= 2;
|
|
12515
12305
|
bits -= 2;
|
|
12516
12306
|
} else if (here_val === 17) {
|
|
@@ -12526,7 +12316,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
12526
12316
|
hold >>>= here_bits;
|
|
12527
12317
|
bits -= here_bits;
|
|
12528
12318
|
len = 0;
|
|
12529
|
-
|
|
12319
|
+
copy2 = 3 + (hold & 7);
|
|
12530
12320
|
hold >>>= 3;
|
|
12531
12321
|
bits -= 3;
|
|
12532
12322
|
} else {
|
|
@@ -12542,16 +12332,16 @@ var inflate$2 = (strm, flush) => {
|
|
|
12542
12332
|
hold >>>= here_bits;
|
|
12543
12333
|
bits -= here_bits;
|
|
12544
12334
|
len = 0;
|
|
12545
|
-
|
|
12335
|
+
copy2 = 11 + (hold & 127);
|
|
12546
12336
|
hold >>>= 7;
|
|
12547
12337
|
bits -= 7;
|
|
12548
12338
|
}
|
|
12549
|
-
if (state.have +
|
|
12339
|
+
if (state.have + copy2 > state.nlen + state.ndist) {
|
|
12550
12340
|
strm.msg = "invalid bit length repeat";
|
|
12551
12341
|
state.mode = BAD;
|
|
12552
12342
|
break;
|
|
12553
12343
|
}
|
|
12554
|
-
while (
|
|
12344
|
+
while (copy2--) {
|
|
12555
12345
|
state.lens[state.have++] = len;
|
|
12556
12346
|
}
|
|
12557
12347
|
}
|
|
@@ -12770,39 +12560,39 @@ var inflate$2 = (strm, flush) => {
|
|
|
12770
12560
|
if (left === 0) {
|
|
12771
12561
|
break inf_leave;
|
|
12772
12562
|
}
|
|
12773
|
-
|
|
12774
|
-
if (state.offset >
|
|
12775
|
-
|
|
12776
|
-
if (
|
|
12563
|
+
copy2 = _out - left;
|
|
12564
|
+
if (state.offset > copy2) {
|
|
12565
|
+
copy2 = state.offset - copy2;
|
|
12566
|
+
if (copy2 > state.whave) {
|
|
12777
12567
|
if (state.sane) {
|
|
12778
12568
|
strm.msg = "invalid distance too far back";
|
|
12779
12569
|
state.mode = BAD;
|
|
12780
12570
|
break;
|
|
12781
12571
|
}
|
|
12782
12572
|
}
|
|
12783
|
-
if (
|
|
12784
|
-
|
|
12785
|
-
from = state.wsize -
|
|
12573
|
+
if (copy2 > state.wnext) {
|
|
12574
|
+
copy2 -= state.wnext;
|
|
12575
|
+
from = state.wsize - copy2;
|
|
12786
12576
|
} else {
|
|
12787
|
-
from = state.wnext -
|
|
12577
|
+
from = state.wnext - copy2;
|
|
12788
12578
|
}
|
|
12789
|
-
if (
|
|
12790
|
-
|
|
12579
|
+
if (copy2 > state.length) {
|
|
12580
|
+
copy2 = state.length;
|
|
12791
12581
|
}
|
|
12792
12582
|
from_source = state.window;
|
|
12793
12583
|
} else {
|
|
12794
12584
|
from_source = output;
|
|
12795
12585
|
from = put - state.offset;
|
|
12796
|
-
|
|
12586
|
+
copy2 = state.length;
|
|
12797
12587
|
}
|
|
12798
|
-
if (
|
|
12799
|
-
|
|
12588
|
+
if (copy2 > left) {
|
|
12589
|
+
copy2 = left;
|
|
12800
12590
|
}
|
|
12801
|
-
left -=
|
|
12802
|
-
state.length -=
|
|
12591
|
+
left -= copy2;
|
|
12592
|
+
state.length -= copy2;
|
|
12803
12593
|
do {
|
|
12804
12594
|
output[put++] = from_source[from++];
|
|
12805
|
-
} while (--
|
|
12595
|
+
} while (--copy2);
|
|
12806
12596
|
if (state.length === 0) {
|
|
12807
12597
|
state.mode = LEN;
|
|
12808
12598
|
}
|
|
@@ -14693,12 +14483,12 @@ var Model = class _Model extends import_index2.default {
|
|
|
14693
14483
|
});
|
|
14694
14484
|
return value2;
|
|
14695
14485
|
} catch {
|
|
14696
|
-
const
|
|
14697
|
-
Object.defineProperty(
|
|
14486
|
+
const copy2 = { ...value2 };
|
|
14487
|
+
Object.defineProperty(copy2, SYM_EXPANDED_REF, {
|
|
14698
14488
|
enumerable: true,
|
|
14699
14489
|
value: true
|
|
14700
14490
|
});
|
|
14701
|
-
return
|
|
14491
|
+
return copy2;
|
|
14702
14492
|
}
|
|
14703
14493
|
}
|
|
14704
14494
|
/**
|
|
@@ -16385,6 +16175,24 @@ function sealHistory(key, routing, message, random) {
|
|
|
16385
16175
|
|
|
16386
16176
|
// src/history.ts
|
|
16387
16177
|
var HISTORY_PATH = "/dashup/history";
|
|
16178
|
+
var HISTORY_KEPT = 300;
|
|
16179
|
+
var opaque = (key, value2) => createHmac2("sha256", Buffer.from(key, "hex")).update(JSON.stringify(value2)).digest("hex");
|
|
16180
|
+
var historyMessages = (key, project, thread, messages5) => splitHistoryMessages(messages5, (id) => opaque(key, ["history-message", project, thread, id]));
|
|
16181
|
+
var copy = (messages5) => new Map(
|
|
16182
|
+
messages5.slice(-HISTORY_PAGE_ITEMS).map((message) => [message.id, JSON.stringify(message)])
|
|
16183
|
+
);
|
|
16184
|
+
function changes(copy2, messages5) {
|
|
16185
|
+
const latest = messages5.slice(-HISTORY_PAGE_ITEMS);
|
|
16186
|
+
let known = latest.length;
|
|
16187
|
+
while (known > 0 && !copy2.has(latest[known - 1].id)) known--;
|
|
16188
|
+
if (!known && copy2.size) return [];
|
|
16189
|
+
return latest.filter((message, at) => {
|
|
16190
|
+
const json = JSON.stringify(message);
|
|
16191
|
+
if (copy2.get(message.id) === json || at < known && !copy2.has(message.id)) return false;
|
|
16192
|
+
copy2.set(message.id, json);
|
|
16193
|
+
return true;
|
|
16194
|
+
});
|
|
16195
|
+
}
|
|
16388
16196
|
async function upload(config2, batch) {
|
|
16389
16197
|
if (batch.records.length > HISTORY_BATCH_ITEMS || Buffer.byteLength(JSON.stringify(batch)) > HISTORY_BATCH_BYTES)
|
|
16390
16198
|
throw new Error("The encrypted history upload exceeds the batch limit");
|
|
@@ -16399,8 +16207,10 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16399
16207
|
const local = config2.pairs[pair.id];
|
|
16400
16208
|
const generation = ++local.sequence;
|
|
16401
16209
|
saveConfig(config2);
|
|
16402
|
-
const
|
|
16403
|
-
|
|
16210
|
+
const all = historyMessages(local.key, project, thread.id, messages5);
|
|
16211
|
+
let start = Math.max(0, all.length - HISTORY_KEPT);
|
|
16212
|
+
while (start < all.length && (all[start].part ?? 0) > 0) start++;
|
|
16213
|
+
const fragments = start ? [...historyMessages(local.key, project, thread.id, [EARLIER]), ...all.slice(start)] : all;
|
|
16404
16214
|
const summary = { generation, total: fragments.length, updatedAt: thread.updatedAt };
|
|
16405
16215
|
const batch = {
|
|
16406
16216
|
pairing: pair.id,
|
|
@@ -16416,7 +16226,7 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16416
16226
|
const record = sealHistory(
|
|
16417
16227
|
local.key,
|
|
16418
16228
|
{
|
|
16419
|
-
id: opaque(["history-record", project, thread.id, generation, position]),
|
|
16229
|
+
id: opaque(local.key, ["history-record", project, thread.id, generation, position]),
|
|
16420
16230
|
pairing: pair.id,
|
|
16421
16231
|
sender: pair.device,
|
|
16422
16232
|
recipient: pair.mobile,
|
|
@@ -16445,16 +16255,16 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16445
16255
|
}
|
|
16446
16256
|
|
|
16447
16257
|
// src/harness/live.ts
|
|
16448
|
-
import { promisify as
|
|
16449
|
-
import { execFile as
|
|
16258
|
+
import { promisify as promisify5 } from "node:util";
|
|
16259
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
16450
16260
|
|
|
16451
16261
|
// src/harness/codex.ts
|
|
16452
16262
|
import WebSocket2 from "ws";
|
|
16453
16263
|
import { homedir as homedir4 } from "node:os";
|
|
16454
16264
|
import { connect } from "node:net";
|
|
16455
|
-
import { promisify as
|
|
16265
|
+
import { promisify as promisify2 } from "node:util";
|
|
16456
16266
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
16457
|
-
import { execFile as
|
|
16267
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
16458
16268
|
import { join as join4, isAbsolute as isAbsolute2 } from "node:path";
|
|
16459
16269
|
import { stat, readdir, readFile } from "node:fs/promises";
|
|
16460
16270
|
var REQUEST_MS = 4e3;
|
|
@@ -16478,7 +16288,7 @@ function approval(id, method, params) {
|
|
|
16478
16288
|
const oversized = Buffer.byteLength(detail) > MAX_PREVIEW;
|
|
16479
16289
|
const unavailable = !command2 || params.kind !== void 0 && params.kind !== "command" || typeof params.command !== "string" || !params.command.trim() || oversized;
|
|
16480
16290
|
if (oversized) detail = Buffer.from(detail).subarray(0, MAX_PREVIEW).toString("utf8");
|
|
16481
|
-
if (unavailable) detail = [detail,
|
|
16291
|
+
if (unavailable) detail = [detail, NO_PREVIEW].filter(Boolean).join("\n\n");
|
|
16482
16292
|
return {
|
|
16483
16293
|
id,
|
|
16484
16294
|
title: command2 ? "Allow command?" : "Allow file changes?",
|
|
@@ -16774,7 +16584,7 @@ var Codex = class {
|
|
|
16774
16584
|
// Codex's own daemon runs the threads Dashup starts or resumes, so they outlive this connector.
|
|
16775
16585
|
async start(cwd, text3, saved) {
|
|
16776
16586
|
const path = process.env.DASHUP_CODEX_SOCKET || join4(codexHome(), ...SOCKET_PARTS);
|
|
16777
|
-
await
|
|
16587
|
+
await promisify2(execFile2)("codex", ["app-server", "daemon", "start"], {
|
|
16778
16588
|
timeout: 6e4,
|
|
16779
16589
|
shell: process.platform === "win32"
|
|
16780
16590
|
});
|
|
@@ -16817,8 +16627,8 @@ var Codex = class {
|
|
|
16817
16627
|
// src/harness/headless.ts
|
|
16818
16628
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
16819
16629
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
16820
|
-
import { createInterface as
|
|
16821
|
-
import { spawn as
|
|
16630
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
16631
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
16822
16632
|
var CHOICES = [
|
|
16823
16633
|
{ id: "accept", label: "Allow once" },
|
|
16824
16634
|
{ id: "decline", label: "Reject" }
|
|
@@ -16941,7 +16751,7 @@ var Session = class {
|
|
|
16941
16751
|
this.changed();
|
|
16942
16752
|
return Promise.resolve();
|
|
16943
16753
|
}
|
|
16944
|
-
const turn = this.turn =
|
|
16754
|
+
const turn = this.turn = spawn2(
|
|
16945
16755
|
"claude",
|
|
16946
16756
|
[
|
|
16947
16757
|
"-p",
|
|
@@ -16964,7 +16774,7 @@ var Session = class {
|
|
|
16964
16774
|
errors = (errors + String(chunk)).slice(-2e3);
|
|
16965
16775
|
});
|
|
16966
16776
|
const started = new Promise((resolve3, reject) => {
|
|
16967
|
-
|
|
16777
|
+
createInterface2({ input: turn.stdout }).on("line", (line) => {
|
|
16968
16778
|
if (this.receive(line)) resolve3();
|
|
16969
16779
|
});
|
|
16970
16780
|
const end = (reason) => {
|
|
@@ -16996,7 +16806,7 @@ var Session = class {
|
|
|
16996
16806
|
else if (message.type === "assistant") {
|
|
16997
16807
|
const text3 = (Array.isArray(message.message?.content) ? message.message.content : []).filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n\n");
|
|
16998
16808
|
if (!text3) return false;
|
|
16999
|
-
this.messages.push({ id: String(message.
|
|
16809
|
+
this.messages.push({ id: String(message.uuid ?? randomUUID3()), role: "assistant", text: text3 });
|
|
17000
16810
|
} else if (message.type === "control_request" && message.request?.subtype === "can_use_tool") {
|
|
17001
16811
|
const id = randomUUID3();
|
|
17002
16812
|
this.approvals.set(id, {
|
|
@@ -17218,9 +17028,9 @@ var Claude = class {
|
|
|
17218
17028
|
import Socket3 from "ws";
|
|
17219
17029
|
import { homedir as homedir5 } from "node:os";
|
|
17220
17030
|
import { connect as connect3 } from "node:net";
|
|
17221
|
-
import { promisify as
|
|
17222
|
-
import { fileURLToPath
|
|
17223
|
-
import { execFile as
|
|
17031
|
+
import { promisify as promisify3 } from "node:util";
|
|
17032
|
+
import { fileURLToPath } from "node:url";
|
|
17033
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
17224
17034
|
import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
|
|
17225
17035
|
import { stat as stat2, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
17226
17036
|
import { join as join6, dirname as dirname4, basename as basename2, isAbsolute as isAbsolute3 } from "node:path";
|
|
@@ -17233,7 +17043,7 @@ var MAX_METADATA = 64 * 1024;
|
|
|
17233
17043
|
var MAX_HOSTS = 16;
|
|
17234
17044
|
var MAX_SESSIONS3 = 100;
|
|
17235
17045
|
var ROOT_CHANNEL = "ahp-root://";
|
|
17236
|
-
var
|
|
17046
|
+
var run = promisify3(execFile3);
|
|
17237
17047
|
var CONFIRMATION_CHOICES = [
|
|
17238
17048
|
{ id: "approve", label: "Allow once", kind: "approve" },
|
|
17239
17049
|
{ id: "deny", label: "Reject", kind: "deny" }
|
|
@@ -17253,7 +17063,7 @@ async function privateFile(path, limit) {
|
|
|
17253
17063
|
const encodedPath = Buffer.from(path).toString("base64");
|
|
17254
17064
|
const script = `$p=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encodedPath}')); $a=Get-Acl -LiteralPath $p -ErrorAction Stop; $sid=[Security.Principal.SecurityIdentifier]; $me=[Security.Principal.WindowsIdentity]::GetCurrent().User.Value; $ok=$a.GetOwner($sid).Value -eq $me; foreach($r in $a.Access){if($r.AccessControlType -eq 'Allow' -and $r.IdentityReference.Translate($sid).Value -notin @($me,'S-1-5-18','S-1-5-32-544')){$ok=$false}}; $ok`;
|
|
17255
17065
|
try {
|
|
17256
|
-
const { stdout } = await
|
|
17066
|
+
const { stdout } = await run(
|
|
17257
17067
|
"powershell.exe",
|
|
17258
17068
|
[
|
|
17259
17069
|
"-NoProfile",
|
|
@@ -17322,7 +17132,7 @@ function directory(state) {
|
|
|
17322
17132
|
const uri = state.workingDirectories?.[0];
|
|
17323
17133
|
if (typeof uri !== "string" || !uri.startsWith("file:")) return;
|
|
17324
17134
|
try {
|
|
17325
|
-
return
|
|
17135
|
+
return fileURLToPath(uri);
|
|
17326
17136
|
} catch {
|
|
17327
17137
|
return;
|
|
17328
17138
|
}
|
|
@@ -17343,7 +17153,7 @@ function confirmation(toolCall) {
|
|
|
17343
17153
|
const paths = [before, after].filter((uri) => typeof uri === "string");
|
|
17344
17154
|
const names = [...new Set(paths)].map((uri) => {
|
|
17345
17155
|
try {
|
|
17346
|
-
return
|
|
17156
|
+
return fileURLToPath(uri);
|
|
17347
17157
|
} catch {
|
|
17348
17158
|
return uri;
|
|
17349
17159
|
}
|
|
@@ -17370,8 +17180,7 @@ function confirmation(toolCall) {
|
|
|
17370
17180
|
})) : [];
|
|
17371
17181
|
if (unavailable) choices = choices.filter((choice) => choice.kind === "deny");
|
|
17372
17182
|
if (oversized) detail = Buffer.from(detail).subarray(0, MAX_PREVIEW).toString("utf8");
|
|
17373
|
-
if (unavailable || !choices.length)
|
|
17374
|
-
detail = [detail, SCREEN_PREVIEW].filter(Boolean).join("\n\n");
|
|
17183
|
+
if (unavailable || !choices.length) detail = [detail, NO_PREVIEW].filter(Boolean).join("\n\n");
|
|
17375
17184
|
return { detail, choices };
|
|
17376
17185
|
}
|
|
17377
17186
|
function messages3(chat) {
|
|
@@ -17715,11 +17524,11 @@ var VSCode = class {
|
|
|
17715
17524
|
|
|
17716
17525
|
// src/harness/opencode.ts
|
|
17717
17526
|
import { join as join7 } from "node:path";
|
|
17718
|
-
import { promisify as
|
|
17527
|
+
import { promisify as promisify4 } from "node:util";
|
|
17719
17528
|
import { createHash as createHash4 } from "node:crypto";
|
|
17720
|
-
import { execFile as
|
|
17529
|
+
import { execFile as execFile4, spawn as spawn3 } from "node:child_process";
|
|
17721
17530
|
import { stat as stat3, readlink, readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
|
|
17722
|
-
var
|
|
17531
|
+
var run2 = promisify4(execFile4);
|
|
17723
17532
|
var REQUEST_MS4 = 4e3;
|
|
17724
17533
|
var MAX_BYTES3 = 8 * 1024 * 1024;
|
|
17725
17534
|
var MAX_SESSIONS4 = 100;
|
|
@@ -17799,7 +17608,7 @@ async function endpoints() {
|
|
|
17799
17608
|
if (process.env.DASHUP_LIVE_DISCOVERY === "0") return [];
|
|
17800
17609
|
if (process.platform === "linux") return linuxEndpoints();
|
|
17801
17610
|
if (process.platform === "darwin") {
|
|
17802
|
-
const { stdout } = await
|
|
17611
|
+
const { stdout } = await run2(
|
|
17803
17612
|
"lsof",
|
|
17804
17613
|
["-nP", "-a", "-c", "opencode", "-iTCP", "-sTCP:LISTEN", "-Fn"],
|
|
17805
17614
|
{ timeout: REQUEST_MS4, maxBuffer: MAX_BYTES3 }
|
|
@@ -17816,7 +17625,7 @@ async function endpoints() {
|
|
|
17816
17625
|
}
|
|
17817
17626
|
if (process.platform === "win32") {
|
|
17818
17627
|
const script = "$ids = @(Get-Process opencode -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Id); Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue | Where-Object { $ids -contains $_.OwningProcess } | Select-Object -ExpandProperty LocalPort";
|
|
17819
|
-
const { stdout } = await
|
|
17628
|
+
const { stdout } = await run2(
|
|
17820
17629
|
"powershell.exe",
|
|
17821
17630
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
17822
17631
|
{ timeout: REQUEST_MS4, maxBuffer: MAX_BYTES3 }
|
|
@@ -17897,7 +17706,7 @@ ${always.join("\n")}` : ""
|
|
|
17897
17706
|
}
|
|
17898
17707
|
if (unavailable) detail += `
|
|
17899
17708
|
|
|
17900
|
-
${
|
|
17709
|
+
${NO_PREVIEW}`;
|
|
17901
17710
|
return {
|
|
17902
17711
|
id: permission.id,
|
|
17903
17712
|
title: `Allow ${String(permission.permission ?? "tool")}?`,
|
|
@@ -18098,7 +17907,7 @@ var Server = class {
|
|
|
18098
17907
|
if (!permissionApproval(permission, history2, cwd).choices.some(
|
|
18099
17908
|
(value2) => value2.id === choice
|
|
18100
17909
|
))
|
|
18101
|
-
throw new Error(
|
|
17910
|
+
throw new Error(NO_PREVIEW);
|
|
18102
17911
|
}
|
|
18103
17912
|
await this.request(`/permission/${encodeURIComponent(approvalId)}/reply`, cwd, {
|
|
18104
17913
|
reply: choice
|
|
@@ -18185,7 +17994,7 @@ var Server = class {
|
|
|
18185
17994
|
}
|
|
18186
17995
|
};
|
|
18187
17996
|
function serve(cwd) {
|
|
18188
|
-
const child =
|
|
17997
|
+
const child = spawn3("opencode", ["serve", "--hostname", "127.0.0.1", "--port", "0"], {
|
|
18189
17998
|
cwd,
|
|
18190
17999
|
stdio: ["ignore", "pipe", "ignore"],
|
|
18191
18000
|
shell: process.platform === "win32"
|
|
@@ -18310,7 +18119,7 @@ var LiveHarnesses = class {
|
|
|
18310
18119
|
async agents() {
|
|
18311
18120
|
const harnesses = ["claude", "codex", "opencode"];
|
|
18312
18121
|
const runs = await Promise.all(
|
|
18313
|
-
harnesses.map((harness) =>
|
|
18122
|
+
harnesses.map((harness) => promisify5(execFile5)(harness, ["--version"], {
|
|
18314
18123
|
timeout: 1e4,
|
|
18315
18124
|
shell: process.platform === "win32"
|
|
18316
18125
|
}).then(
|
|
@@ -18363,10 +18172,10 @@ var LiveHarnesses = class {
|
|
|
18363
18172
|
|
|
18364
18173
|
// src/harness/queue.ts
|
|
18365
18174
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18366
|
-
import { spawn as
|
|
18175
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
18367
18176
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
18368
18177
|
import { join as join8, dirname as dirname5, basename as basename3 } from "node:path";
|
|
18369
|
-
import { statSync as statSync2, existsSync as
|
|
18178
|
+
import { statSync as statSync2, existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync4, readlinkSync, realpathSync as realpathSync2 } from "node:fs";
|
|
18370
18179
|
var CODEX_VERSION = "0.160.1";
|
|
18371
18180
|
var REQUEST_MS5 = 1e4;
|
|
18372
18181
|
var MAX_BYTES4 = 1024 * 1024;
|
|
@@ -18387,7 +18196,7 @@ function packageVersion(executable) {
|
|
|
18387
18196
|
let directory2 = dirname5(executable);
|
|
18388
18197
|
for (let depth = 0; depth < 8; depth++, directory2 = dirname5(directory2)) {
|
|
18389
18198
|
const path = join8(directory2, "package.json");
|
|
18390
|
-
if (!
|
|
18199
|
+
if (!existsSync4(path)) continue;
|
|
18391
18200
|
const value2 = JSON.parse(readFileSync4(path, "utf8"));
|
|
18392
18201
|
if (value2.name?.startsWith("@openai/codex-")) return value2.version;
|
|
18393
18202
|
}
|
|
@@ -18488,7 +18297,7 @@ function validateEvidence(owner) {
|
|
|
18488
18297
|
if (realpathSync2.native(owner.home) !== owner.home || process.getuid && statSync2(owner.home).uid !== process.getuid() || !sameFile(identity(owner.queue.path), owner.queue) || !sameFile(identity(owner.rollout.path), owner.rollout))
|
|
18489
18298
|
throw new Error("The original native history storage changed");
|
|
18490
18299
|
const path = join8(owner.home, "thread_history_1.sqlite");
|
|
18491
|
-
if (
|
|
18300
|
+
if (existsSync4(path) && identity(path).path !== path)
|
|
18492
18301
|
throw new Error("Native history is outside the original home");
|
|
18493
18302
|
}
|
|
18494
18303
|
function nativeReceipt(owner, clientId) {
|
|
@@ -18505,7 +18314,7 @@ function nativeReceipt(owner, clientId) {
|
|
|
18505
18314
|
queue.close();
|
|
18506
18315
|
}
|
|
18507
18316
|
const path = join8(owner.home, "thread_history_1.sqlite");
|
|
18508
|
-
if (!
|
|
18317
|
+
if (!existsSync4(path)) return { queueId };
|
|
18509
18318
|
const history = new DatabaseSync3(path, { readOnly: true });
|
|
18510
18319
|
try {
|
|
18511
18320
|
const rows = history.prepare(
|
|
@@ -18560,7 +18369,7 @@ var Writer = class {
|
|
|
18560
18369
|
tail = "";
|
|
18561
18370
|
pending = /* @__PURE__ */ new Map();
|
|
18562
18371
|
constructor(owner) {
|
|
18563
|
-
this.process =
|
|
18372
|
+
this.process = spawn4(owner.executable, ["app-server", "--listen", "stdio://"], {
|
|
18564
18373
|
cwd: owner.cwd,
|
|
18565
18374
|
env: { ...process.env, CODEX_HOME: owner.home },
|
|
18566
18375
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -18719,6 +18528,8 @@ var NativeQueue = class {
|
|
|
18719
18528
|
// src/connector.ts
|
|
18720
18529
|
var EPHEMERAL_TTL_MS = 6e4;
|
|
18721
18530
|
var HOME_RECENT = 20;
|
|
18531
|
+
var FOLLOW_MS = 5 * 6e4;
|
|
18532
|
+
var RESCAN_MS = 1e3;
|
|
18722
18533
|
var Command = z2.object({
|
|
18723
18534
|
type: z2.literal("command"),
|
|
18724
18535
|
requestId: z2.string().min(1),
|
|
@@ -18731,8 +18542,6 @@ var Command = z2.object({
|
|
|
18731
18542
|
z2.object({ kind: z2.literal("thread.queue"), text: z2.string().min(1).max(1e5) }).strict(),
|
|
18732
18543
|
z2.object({ kind: z2.literal("thread.stop") }).strict(),
|
|
18733
18544
|
z2.object({ kind: z2.literal("approval.respond"), approvalId: z2.string(), choice: z2.string() }).strict(),
|
|
18734
|
-
z2.object({ kind: z2.literal("screen.start"), sessionId: z2.string().min(1).max(128) }).strict(),
|
|
18735
|
-
z2.object({ kind: z2.literal("screen.stop"), sessionId: z2.string().min(1).max(128) }).strict(),
|
|
18736
18545
|
z2.object({
|
|
18737
18546
|
kind: z2.literal("session.start"),
|
|
18738
18547
|
harness: z2.enum(["claude", "codex", "opencode"]),
|
|
@@ -18758,31 +18567,13 @@ var Connector = class {
|
|
|
18758
18567
|
autoConnect: false
|
|
18759
18568
|
});
|
|
18760
18569
|
provideClient(() => this.client);
|
|
18761
|
-
this.
|
|
18762
|
-
config2,
|
|
18763
|
-
(pair) => this.isActive(pair.id),
|
|
18764
|
-
(pair, content) => this.send(pair, content)
|
|
18765
|
-
);
|
|
18766
|
-
this.discovery = new Discovery(
|
|
18767
|
-
config2.device.id,
|
|
18768
|
-
{
|
|
18769
|
-
read: true,
|
|
18770
|
-
send: false,
|
|
18771
|
-
queue: false,
|
|
18772
|
-
stop: false,
|
|
18773
|
-
approve: false,
|
|
18774
|
-
screen: config2.screen && Viewer.available(),
|
|
18775
|
-
control: config2.control && controlAvailable()
|
|
18776
|
-
},
|
|
18777
|
-
config2.localSecret
|
|
18778
|
-
);
|
|
18570
|
+
this.discovery = new Discovery(config2.device.id, config2.localSecret);
|
|
18779
18571
|
this.pairings = createLiveQuery(() => this.client.bind(Pairing).where({ device: config2.device.id }).whereNull("revokedAt").clearLimit());
|
|
18780
18572
|
this.relays = createLiveQuery(() => this.client.bind(Relay).where({ recipient: config2.device.id }).orderBy("createdAt", "desc").orderBy("id", "desc").clearLimit());
|
|
18781
18573
|
}
|
|
18782
18574
|
config;
|
|
18783
18575
|
client;
|
|
18784
18576
|
discovery;
|
|
18785
|
-
viewer;
|
|
18786
18577
|
pairings;
|
|
18787
18578
|
relays;
|
|
18788
18579
|
nativeQueue;
|
|
@@ -18793,18 +18584,30 @@ var Connector = class {
|
|
|
18793
18584
|
retry;
|
|
18794
18585
|
stopWatching = () => {
|
|
18795
18586
|
};
|
|
18796
|
-
|
|
18797
|
-
|
|
18587
|
+
rescanning;
|
|
18588
|
+
stopped = false;
|
|
18589
|
+
rescan = () => {
|
|
18590
|
+
if (this.rescanning) return;
|
|
18591
|
+
this.rescanning = setTimeout(() => {
|
|
18592
|
+
this.rescanning = void 0;
|
|
18593
|
+
void this.queueChange().catch(() => {
|
|
18594
|
+
});
|
|
18595
|
+
}, RESCAN_MS);
|
|
18596
|
+
this.rescanning.unref();
|
|
18597
|
+
};
|
|
18798
18598
|
releases = [];
|
|
18799
18599
|
announced = /* @__PURE__ */ new Set();
|
|
18800
18600
|
listed = /* @__PURE__ */ new Map();
|
|
18801
18601
|
listedProjects = /* @__PURE__ */ new Map();
|
|
18802
18602
|
overviewQueued = false;
|
|
18603
|
+
pendingChange;
|
|
18803
18604
|
// The agents that can start sessions here.
|
|
18804
18605
|
startable = [];
|
|
18805
18606
|
snapshots = /* @__PURE__ */ new Set();
|
|
18806
18607
|
nativeSnapshots = /* @__PURE__ */ new Map();
|
|
18807
18608
|
queueReceipts = /* @__PURE__ */ new Map();
|
|
18609
|
+
// The conversations phones are reading, by pairing and thread.
|
|
18610
|
+
follows = /* @__PURE__ */ new Map();
|
|
18808
18611
|
// The content key behind each pairing code on show, until a phone pairs with it.
|
|
18809
18612
|
invitations = /* @__PURE__ */ new Map();
|
|
18810
18613
|
async start() {
|
|
@@ -18821,8 +18624,6 @@ var Connector = class {
|
|
|
18821
18624
|
this.releases.push(this.harnesses.watch(this.rescan));
|
|
18822
18625
|
this.releases.push(
|
|
18823
18626
|
this.pairings.retain(() => {
|
|
18824
|
-
if (this.pairings.snapshot().status !== "ready") this.viewer.close();
|
|
18825
|
-
else this.viewer.revoke(new Set(this.activePairs().map((pair) => pair.id)));
|
|
18826
18627
|
this.work = this.work.then(async () => {
|
|
18827
18628
|
if (this.pairings.snapshot().status !== "ready") return;
|
|
18828
18629
|
for (const pair of this.activePairs()) {
|
|
@@ -18902,7 +18703,6 @@ var Connector = class {
|
|
|
18902
18703
|
this.client.send("dashup:presence", { device: this.config.device.id });
|
|
18903
18704
|
});
|
|
18904
18705
|
this.client.on("error", () => this.report(new Error("Connection interrupted. Dashup will reconnect.")));
|
|
18905
|
-
this.client.on("disconnected", () => this.viewer.close());
|
|
18906
18706
|
await this.client.reconnect();
|
|
18907
18707
|
process.stdout.write(
|
|
18908
18708
|
`Dashup is connected. ${this.discovery.projects.size} existing projects found.
|
|
@@ -18916,29 +18716,16 @@ var Connector = class {
|
|
|
18916
18716
|
return invite;
|
|
18917
18717
|
}
|
|
18918
18718
|
stop() {
|
|
18719
|
+
this.stopped = true;
|
|
18919
18720
|
this.stopWatching();
|
|
18920
18721
|
for (const release of this.releases.splice(0)) release();
|
|
18921
18722
|
clearTimeout(this.retry);
|
|
18723
|
+
clearTimeout(this.rescanning);
|
|
18922
18724
|
this.client.dispose();
|
|
18923
|
-
this.viewer.close();
|
|
18924
18725
|
this.nativeQueue.stop();
|
|
18925
18726
|
this.harnesses.stop();
|
|
18926
18727
|
for (const agent of this.agents.values()) agent.socket.destroy();
|
|
18927
18728
|
}
|
|
18928
|
-
async permissions(screen, control) {
|
|
18929
|
-
this.config.screen = screen;
|
|
18930
|
-
this.config.control = screen && control;
|
|
18931
|
-
this.discovery.capabilities = {
|
|
18932
|
-
...this.discovery.capabilities,
|
|
18933
|
-
screen: screen && Viewer.available(),
|
|
18934
|
-
control: this.config.control && controlAvailable()
|
|
18935
|
-
};
|
|
18936
|
-
const sessions = screen ? [] : [...this.viewer.sessions];
|
|
18937
|
-
if (!screen) this.viewer.close();
|
|
18938
|
-
saveConfig(this.config);
|
|
18939
|
-
for (const [id, current] of sessions) await this.viewer.sendClose(id, current);
|
|
18940
|
-
await this.queueChange();
|
|
18941
|
-
}
|
|
18942
18729
|
activePairs() {
|
|
18943
18730
|
return this.pairings.snapshot().items;
|
|
18944
18731
|
}
|
|
@@ -18967,7 +18754,7 @@ var Connector = class {
|
|
|
18967
18754
|
);
|
|
18968
18755
|
if (envelope.ciphertext.length > MAX_ENVELOPE_BYTES * 2)
|
|
18969
18756
|
throw new Error(
|
|
18970
|
-
"This update exceeds the encrypted message limit.
|
|
18757
|
+
"This update exceeds the encrypted message limit. View the full conversation on your computer."
|
|
18971
18758
|
);
|
|
18972
18759
|
this.config.outbox.push(envelope);
|
|
18973
18760
|
saveConfig(this.config);
|
|
@@ -18980,7 +18767,7 @@ var Connector = class {
|
|
|
18980
18767
|
saveConfig(this.config);
|
|
18981
18768
|
}
|
|
18982
18769
|
flush() {
|
|
18983
|
-
const
|
|
18770
|
+
const run4 = this.drain.catch(() => {
|
|
18984
18771
|
}).then(async () => {
|
|
18985
18772
|
this.discardLegacySnapshots();
|
|
18986
18773
|
while (this.config.outbox.length && this.client.isConnected) {
|
|
@@ -18990,8 +18777,8 @@ var Connector = class {
|
|
|
18990
18777
|
saveConfig(this.config);
|
|
18991
18778
|
}
|
|
18992
18779
|
});
|
|
18993
|
-
this.drain =
|
|
18994
|
-
return
|
|
18780
|
+
this.drain = run4;
|
|
18781
|
+
return run4.catch((error) => {
|
|
18995
18782
|
this.report(error);
|
|
18996
18783
|
clearTimeout(this.retry);
|
|
18997
18784
|
this.retry = setTimeout(() => {
|
|
@@ -19032,7 +18819,6 @@ var Connector = class {
|
|
|
19032
18819
|
return {
|
|
19033
18820
|
type: "projects",
|
|
19034
18821
|
version,
|
|
19035
|
-
screen: this.discovery.capabilities.screen,
|
|
19036
18822
|
agents: this.startable,
|
|
19037
18823
|
folders: this.discovery.folders(),
|
|
19038
18824
|
projects: [...this.listedProjects.values()],
|
|
@@ -19062,12 +18848,20 @@ var Connector = class {
|
|
|
19062
18848
|
for (const pair of this.activePairs()) await this.listProjects(pair);
|
|
19063
18849
|
}).catch((error) => this.report(error));
|
|
19064
18850
|
}
|
|
18851
|
+
// A change still waiting to start covers every event after it, so a burst of writes to a
|
|
18852
|
+
// transcript costs one rescan rather than one each.
|
|
19065
18853
|
queueChange() {
|
|
19066
|
-
|
|
19067
|
-
|
|
19068
|
-
|
|
18854
|
+
if (this.pendingChange) return this.pendingChange;
|
|
18855
|
+
const run4 = this.work.then(() => {
|
|
18856
|
+
this.pendingChange = void 0;
|
|
18857
|
+
return this.changed();
|
|
18858
|
+
});
|
|
18859
|
+
this.pendingChange = run4;
|
|
18860
|
+
this.work = run4.catch((error) => this.report(error));
|
|
18861
|
+
return run4;
|
|
19069
18862
|
}
|
|
19070
18863
|
async changed() {
|
|
18864
|
+
if (this.stopped) return;
|
|
19071
18865
|
const previous = new Map(
|
|
19072
18866
|
[...this.discovery.projects.values()].map((project) => [project.id, project])
|
|
19073
18867
|
);
|
|
@@ -19102,9 +18896,19 @@ var Connector = class {
|
|
|
19102
18896
|
const projects = [...this.discovery.projects.values()];
|
|
19103
18897
|
this.stopWatching();
|
|
19104
18898
|
this.stopWatching = this.discovery.watch(this.rescan);
|
|
18899
|
+
await this.stream();
|
|
19105
18900
|
for (const project of projects) {
|
|
19106
|
-
|
|
19107
|
-
|
|
18901
|
+
const before = previous.get(project.id);
|
|
18902
|
+
const sent = new Map(before?.threads.map((thread) => [thread.id, JSON.stringify(thread)]));
|
|
18903
|
+
const changed = project.threads.filter(
|
|
18904
|
+
(thread) => sent.get(thread.id) !== JSON.stringify(thread)
|
|
18905
|
+
);
|
|
18906
|
+
const whole = !before || project.threads.filter((thread) => sent.has(thread.id)).length < sent.size || JSON.stringify({ ...before, threads: [] }) !== JSON.stringify({ ...project, threads: [] });
|
|
18907
|
+
for (const pair of this.activePairs())
|
|
18908
|
+
if (whole) await this.send(pair, { type: "project", project });
|
|
18909
|
+
else
|
|
18910
|
+
for (const thread of changed)
|
|
18911
|
+
if (!current.has(thread.id)) await this.thread(pair, project, thread);
|
|
19108
18912
|
}
|
|
19109
18913
|
for (const { project, thread, session } of live) {
|
|
19110
18914
|
const snapshot = this.threadState(project, thread);
|
|
@@ -19127,6 +18931,40 @@ var Connector = class {
|
|
|
19127
18931
|
for (const pair of this.activePairs()) await this.listProjects(pair);
|
|
19128
18932
|
await this.publishQueueReceipts();
|
|
19129
18933
|
}
|
|
18934
|
+
// A phone reading a conversation gets its messages as they change, before anything that says the
|
|
18935
|
+
// conversation changed, so it never asks for a copy it already has.
|
|
18936
|
+
async stream() {
|
|
18937
|
+
for (const [key, follow] of this.follows) {
|
|
18938
|
+
const thread = this.discovery.projects.get(follow.project)?.threads.find((item) => item.id === follow.thread);
|
|
18939
|
+
let messages5;
|
|
18940
|
+
try {
|
|
18941
|
+
if (thread && follow.until > Date.now() && activePair(this.config, follow.pair))
|
|
18942
|
+
messages5 = changes(
|
|
18943
|
+
follow.copy,
|
|
18944
|
+
this.discovery.read(thread, this.attached(thread)?.messages)
|
|
18945
|
+
);
|
|
18946
|
+
} catch {
|
|
18947
|
+
}
|
|
18948
|
+
if (!thread || !messages5) {
|
|
18949
|
+
this.follows.delete(key);
|
|
18950
|
+
continue;
|
|
18951
|
+
}
|
|
18952
|
+
if (!messages5.length && thread.updatedAt === follow.updatedAt) continue;
|
|
18953
|
+
follow.updatedAt = thread.updatedAt;
|
|
18954
|
+
await this.send(follow.pair, {
|
|
18955
|
+
type: "thread-tail",
|
|
18956
|
+
projectId: follow.project,
|
|
18957
|
+
threadId: follow.thread,
|
|
18958
|
+
updatedAt: follow.updatedAt,
|
|
18959
|
+
messages: historyMessages(
|
|
18960
|
+
this.config.pairs[follow.pair.id].key,
|
|
18961
|
+
follow.project,
|
|
18962
|
+
follow.thread,
|
|
18963
|
+
messages5
|
|
18964
|
+
)
|
|
18965
|
+
});
|
|
18966
|
+
}
|
|
18967
|
+
}
|
|
19130
18968
|
// A saved session nothing else has open continues headless when its agent runs here. One a
|
|
19131
18969
|
// native process owns takes queued messages instead.
|
|
19132
18970
|
refreshResumes() {
|
|
@@ -19178,38 +19016,23 @@ var Connector = class {
|
|
|
19178
19016
|
}
|
|
19179
19017
|
async receive(envelope) {
|
|
19180
19018
|
const pair = this.activePairs().find((item) => item.id === envelope.pairing);
|
|
19181
|
-
if (!pair || !activePair(this.config, pair) || envelope.sender !== pair.mobile || envelope.recipient !== pair.device ||
|
|
19019
|
+
if (!pair || !activePair(this.config, pair) || envelope.sender !== pair.mobile || envelope.recipient !== pair.device || envelope.channel !== "command")
|
|
19182
19020
|
return;
|
|
19183
19021
|
const key = this.config.pairs[pair.id];
|
|
19184
19022
|
if (envelope.sequence <= key.received) return;
|
|
19185
19023
|
const content = open(key.key, envelope);
|
|
19186
|
-
if (content.type !== "command"
|
|
19024
|
+
if (content.type !== "command" || content.projectId !== envelope.project || content.requestId !== envelope.id)
|
|
19187
19025
|
return;
|
|
19188
|
-
|
|
19189
|
-
if (content.type === "command") Command.parse(content);
|
|
19026
|
+
Command.parse(content);
|
|
19190
19027
|
key.received = envelope.sequence;
|
|
19191
|
-
|
|
19192
|
-
this.config.unconfirmed[content.requestId] = {
|
|
19193
|
-
pairing: pair.id,
|
|
19194
|
-
project: content.projectId
|
|
19195
|
-
};
|
|
19028
|
+
this.config.unconfirmed[content.requestId] = { pairing: pair.id, project: content.projectId };
|
|
19196
19029
|
saveConfig(this.config);
|
|
19197
|
-
if (content.type === "signal") {
|
|
19198
|
-
this.viewer.signal(pair, content.projectId, content.sessionId, content.signal);
|
|
19199
|
-
return;
|
|
19200
|
-
}
|
|
19201
|
-
if (content.action.kind === "screen.start") {
|
|
19202
|
-
void this.execute(pair, content, envelope).catch((error) => this.report(error));
|
|
19203
|
-
return;
|
|
19204
|
-
}
|
|
19205
19030
|
await this.execute(pair, content, envelope);
|
|
19206
19031
|
}
|
|
19207
19032
|
async execute(pair, content, envelope) {
|
|
19208
19033
|
let queue = void 0;
|
|
19209
19034
|
try {
|
|
19210
|
-
if (["
|
|
19211
|
-
content.action.kind
|
|
19212
|
-
) && Math.abs(Date.now() - Date.parse(envelope.sentAt)) > EPHEMERAL_TTL_MS)
|
|
19035
|
+
if (["approval.respond", "thread.stop", "session.start"].includes(content.action.kind) && Math.abs(Date.now() - Date.parse(envelope.sentAt)) > EPHEMERAL_TTL_MS)
|
|
19213
19036
|
throw new Error("Command expired. Try again while this computer is online.");
|
|
19214
19037
|
const outcome = await this.command(pair, content);
|
|
19215
19038
|
delete this.config.unconfirmed[content.requestId];
|
|
@@ -19362,12 +19185,20 @@ var Connector = class {
|
|
|
19362
19185
|
if (!thread) throw new Error("Thread not found");
|
|
19363
19186
|
const messages5 = this.discovery.read(thread, this.attached(thread)?.messages);
|
|
19364
19187
|
const history = await publishHistory(this.config, pair, project.id, thread, messages5);
|
|
19188
|
+
this.follows.set(`${pair.id}:${thread.id}`, {
|
|
19189
|
+
pair,
|
|
19190
|
+
project: project.id,
|
|
19191
|
+
thread: thread.id,
|
|
19192
|
+
until: Date.now() + FOLLOW_MS,
|
|
19193
|
+
copy: copy(messages5),
|
|
19194
|
+
updatedAt: thread.updatedAt
|
|
19195
|
+
});
|
|
19365
19196
|
return this.thread(pair, project, thread, history);
|
|
19366
19197
|
}
|
|
19367
19198
|
case "thread.send": {
|
|
19368
19199
|
if (!thread || !thread.capabilities.send)
|
|
19369
19200
|
throw new Error(
|
|
19370
|
-
"This session has no live reply connection.
|
|
19201
|
+
"This session has no live reply connection. Use the existing agent on your computer."
|
|
19371
19202
|
);
|
|
19372
19203
|
const location2 = this.discovery.locations.get(thread.id);
|
|
19373
19204
|
if (location2?.live?.send) return location2.live.send(command2.requestId, command2.action.text);
|
|
@@ -19388,7 +19219,7 @@ var Connector = class {
|
|
|
19388
19219
|
const agent = thread && this.attached(thread);
|
|
19389
19220
|
if (!agent || agent.socket.destroyed)
|
|
19390
19221
|
throw new Error(
|
|
19391
|
-
"This session has no live connection that can stop it.
|
|
19222
|
+
"This session has no live connection that can stop it. Stop the agent on your computer."
|
|
19392
19223
|
);
|
|
19393
19224
|
return commandAcp(agent, command2.requestId, { kind: "cancel" });
|
|
19394
19225
|
}
|
|
@@ -19422,7 +19253,8 @@ var Connector = class {
|
|
|
19422
19253
|
return native.approve(command2.requestId, approval4.id, action.choice);
|
|
19423
19254
|
}
|
|
19424
19255
|
const agent = this.attached(thread);
|
|
19425
|
-
if (!agent)
|
|
19256
|
+
if (!agent)
|
|
19257
|
+
throw new Error("Approval requires an attached ACP session. Answer it on your computer.");
|
|
19426
19258
|
const [request2, approval3] = [...agent.approvals].find(([, item]) => item.id === action.approvalId) ?? [];
|
|
19427
19259
|
if (!approval3?.choices.some((choice) => choice.id === action.choice))
|
|
19428
19260
|
throw new Error("Approval is no longer available");
|
|
@@ -19432,12 +19264,6 @@ var Connector = class {
|
|
|
19432
19264
|
choice: action.choice
|
|
19433
19265
|
});
|
|
19434
19266
|
}
|
|
19435
|
-
case "screen.start":
|
|
19436
|
-
if (!this.config.screen) throw new Error("Enable screen access on this computer first");
|
|
19437
|
-
return this.viewer.start(pair, project.id, command2.action.sessionId);
|
|
19438
|
-
case "screen.stop":
|
|
19439
|
-
this.viewer.stop(pair, command2.action.sessionId);
|
|
19440
|
-
return;
|
|
19441
19267
|
}
|
|
19442
19268
|
}
|
|
19443
19269
|
// Starts an agent in a project, or in a new folder inside one this computer offers. Commands run
|
|
@@ -19478,24 +19304,24 @@ var Connector = class {
|
|
|
19478
19304
|
};
|
|
19479
19305
|
|
|
19480
19306
|
// src/install.ts
|
|
19481
|
-
import { existsSync as
|
|
19307
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
19482
19308
|
import { homedir as homedir6, userInfo } from "node:os";
|
|
19483
|
-
import { promisify as
|
|
19484
|
-
import { fileURLToPath as
|
|
19485
|
-
import { execFile as
|
|
19309
|
+
import { promisify as promisify6 } from "node:util";
|
|
19310
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19311
|
+
import { execFile as execFile6, spawn as spawn5 } from "node:child_process";
|
|
19486
19312
|
import { cp, mkdir as mkdir2, rm, writeFile } from "node:fs/promises";
|
|
19487
19313
|
import { dirname as dirname6, join as join10, relative, sep } from "node:path";
|
|
19488
|
-
var
|
|
19314
|
+
var run3 = promisify6(execFile6);
|
|
19489
19315
|
var xml = (value2) => value2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
19490
19316
|
var NODE_FLAGS = ["--disable-warning=ExperimentalWarning"];
|
|
19491
|
-
var systemctl = (...args2) =>
|
|
19317
|
+
var systemctl = (...args2) => run3("systemctl", ["--user", ...args2], {
|
|
19492
19318
|
env: { ...process.env, XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR ?? `/run/user/${userInfo().uid}` }
|
|
19493
19319
|
});
|
|
19494
19320
|
var serviceFile = () => process.platform === "darwin" ? join10(homedir6(), "Library/LaunchAgents/com.dashup.connector.plist") : process.platform === "win32" ? join10(
|
|
19495
19321
|
process.env.APPDATA ?? join10(homedir6(), "AppData/Roaming"),
|
|
19496
19322
|
"Microsoft/Windows/Start Menu/Programs/Startup/Dashup.vbs"
|
|
19497
19323
|
) : join10(homedir6(), ".config/systemd/user/dashup.service");
|
|
19498
|
-
var startupInstalled = () =>
|
|
19324
|
+
var startupInstalled = () => existsSync5(serviceFile());
|
|
19499
19325
|
function service(platform, node, entry, config2) {
|
|
19500
19326
|
const command2 = [node, ...NODE_FLAGS, entry];
|
|
19501
19327
|
if (platform === "darwin")
|
|
@@ -19523,7 +19349,7 @@ WantedBy=default.target
|
|
|
19523
19349
|
`;
|
|
19524
19350
|
}
|
|
19525
19351
|
async function durableEntry() {
|
|
19526
|
-
const entry =
|
|
19352
|
+
const entry = fileURLToPath2(import.meta.url);
|
|
19527
19353
|
if (!entry.endsWith(`${sep}dist${sep}index.js`))
|
|
19528
19354
|
throw new Error(
|
|
19529
19355
|
"Build the connector before installing startup: pnpm --filter @dashup/connector build"
|
|
@@ -19546,13 +19372,13 @@ async function installStartup() {
|
|
|
19546
19372
|
service(process.platform, process.execPath, await durableEntry(), configPath())
|
|
19547
19373
|
);
|
|
19548
19374
|
if (process.platform === "darwin") {
|
|
19549
|
-
await
|
|
19375
|
+
await run3("launchctl", ["unload", path]).catch(() => {
|
|
19550
19376
|
});
|
|
19551
|
-
await
|
|
19377
|
+
await run3("launchctl", ["load", path]);
|
|
19552
19378
|
} else if (process.platform === "win32") {
|
|
19553
|
-
|
|
19379
|
+
spawn5("wscript.exe", [path], { detached: true, stdio: "ignore" }).unref();
|
|
19554
19380
|
} else {
|
|
19555
|
-
const note = process.env.DISPLAY || process.env.WAYLAND_DISPLAY ? void 0 : await
|
|
19381
|
+
const note = process.env.DISPLAY || process.env.WAYLAND_DISPLAY ? void 0 : await run3("loginctl", ["enable-linger"]).then(
|
|
19556
19382
|
() => void 0,
|
|
19557
19383
|
() => `It stops when you log out. To keep it running, run sudo loginctl enable-linger ${userInfo().username}.`
|
|
19558
19384
|
);
|
|
@@ -19564,7 +19390,7 @@ async function installStartup() {
|
|
|
19564
19390
|
}
|
|
19565
19391
|
async function uninstallStartup() {
|
|
19566
19392
|
const path = serviceFile();
|
|
19567
|
-
if (process.platform === "darwin") await
|
|
19393
|
+
if (process.platform === "darwin") await run3("launchctl", ["unload", path]).catch(() => {
|
|
19568
19394
|
});
|
|
19569
19395
|
else if (process.platform !== "win32")
|
|
19570
19396
|
await systemctl("disable", "dashup.service").catch(() => {
|
|
@@ -19669,22 +19495,6 @@ async function main() {
|
|
|
19669
19495
|
if (!response.ok) throw new Error("Could not stop Dashup");
|
|
19670
19496
|
return;
|
|
19671
19497
|
}
|
|
19672
|
-
if (command === "permissions") {
|
|
19673
|
-
config.screen = args.includes("--screen");
|
|
19674
|
-
config.control = config.screen && args.includes("--control");
|
|
19675
|
-
const remote = await running("/permissions", {
|
|
19676
|
-
method: "POST",
|
|
19677
|
-
body: JSON.stringify({ screen: config.screen, control: config.control }),
|
|
19678
|
-
signal: AbortSignal.timeout(5e3)
|
|
19679
|
-
}).catch(refused);
|
|
19680
|
-
if (remote && !remote.ok) throw new Error("Could not change connector permissions");
|
|
19681
|
-
if (!remote) saveConfig({ ...loadConfig(), screen: config.screen, control: config.control });
|
|
19682
|
-
process.stdout.write(
|
|
19683
|
-
`Screen ${config.screen ? "enabled" : "disabled"}, control ${config.control ? "enabled" : "disabled"}.
|
|
19684
|
-
`
|
|
19685
|
-
);
|
|
19686
|
-
return;
|
|
19687
|
-
}
|
|
19688
19498
|
if (command === "acp") {
|
|
19689
19499
|
const native = args[1] === "--" ? args.slice(2) : args.slice(1);
|
|
19690
19500
|
const [executable, ...agentArgs] = native;
|
|
@@ -19693,18 +19503,7 @@ async function main() {
|
|
|
19693
19503
|
return proxyAcp(config, executable, agentArgs);
|
|
19694
19504
|
}
|
|
19695
19505
|
if (command === "doctor" || command === "projects") {
|
|
19696
|
-
const
|
|
19697
|
-
const discovery = new Discovery(
|
|
19698
|
-
config.device?.id ?? "local",
|
|
19699
|
-
{
|
|
19700
|
-
read: true,
|
|
19701
|
-
send: false,
|
|
19702
|
-
approve: false,
|
|
19703
|
-
screen: config.screen && support.screen,
|
|
19704
|
-
control: config.control && support.control
|
|
19705
|
-
},
|
|
19706
|
-
config.localSecret
|
|
19707
|
-
);
|
|
19506
|
+
const discovery = new Discovery(config.device?.id ?? "local", config.localSecret);
|
|
19708
19507
|
discovery.scan();
|
|
19709
19508
|
const live = new LiveHarnesses();
|
|
19710
19509
|
try {
|
|
@@ -19719,7 +19518,6 @@ async function main() {
|
|
|
19719
19518
|
node: process.version,
|
|
19720
19519
|
paired: Boolean(config.secret),
|
|
19721
19520
|
platform: process.platform,
|
|
19722
|
-
screen: support,
|
|
19723
19521
|
projectCount: projects.length,
|
|
19724
19522
|
harnesses: Object.fromEntries(
|
|
19725
19523
|
Object.keys(HARNESSES).filter((harness) => harness !== "other").map((harness) => [
|
|
@@ -19753,7 +19551,7 @@ async function main() {
|
|
|
19753
19551
|
}
|
|
19754
19552
|
if (!["pair", "watch"].includes(command)) {
|
|
19755
19553
|
process.stdout.write(
|
|
19756
|
-
"Dashup: pair [--account <email>] | watch | projects | alert --project <id> | doctor |
|
|
19554
|
+
"Dashup: pair [--account <email>] | watch | projects | alert --project <id> | doctor | stop | acp -- <original-agent-command> | install | uninstall\n"
|
|
19757
19555
|
);
|
|
19758
19556
|
return;
|
|
19759
19557
|
}
|
|
@@ -19826,7 +19624,7 @@ async function main() {
|
|
|
19826
19624
|
response.end(JSON.stringify(connector.invite()));
|
|
19827
19625
|
return;
|
|
19828
19626
|
}
|
|
19829
|
-
if (
|
|
19627
|
+
if (request2.url !== "/attention" || request2.method !== "POST") {
|
|
19830
19628
|
response.writeHead(404).end();
|
|
19831
19629
|
return;
|
|
19832
19630
|
}
|
|
@@ -19835,12 +19633,6 @@ async function main() {
|
|
|
19835
19633
|
value2 += String(chunk);
|
|
19836
19634
|
if (value2.length > 2e4) throw new Error("Alert too large");
|
|
19837
19635
|
}
|
|
19838
|
-
if (request2.url === "/permissions") {
|
|
19839
|
-
const input2 = z3.object({ screen: z3.boolean(), control: z3.boolean() }).strict().parse(JSON.parse(value2));
|
|
19840
|
-
await connector.permissions(input2.screen, input2.control);
|
|
19841
|
-
response.end("{}");
|
|
19842
|
-
return;
|
|
19843
|
-
}
|
|
19844
19636
|
const input = Attention.parse(JSON.parse(value2));
|
|
19845
19637
|
await connector.attention(
|
|
19846
19638
|
input.projectId,
|