@dashup/connector 0.1.8 → 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 +336 -584
- package/package.json +4 -4
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,282 +4146,19 @@ 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
|
|
4428
4164
|
var EARLIER = {
|
|
@@ -4438,7 +4174,7 @@ function agentHarness(name) {
|
|
|
4438
4174
|
return void 0;
|
|
4439
4175
|
}
|
|
4440
4176
|
var MAX_PREVIEW = 64 * 1024;
|
|
4441
|
-
var
|
|
4177
|
+
var NO_PREVIEW = "Review the full request on your computer before approving.";
|
|
4442
4178
|
|
|
4443
4179
|
// src/harness/history.ts
|
|
4444
4180
|
var SAMPLE_BYTES = 128 * 1024;
|
|
@@ -4482,10 +4218,10 @@ function readRange(path, start, length) {
|
|
|
4482
4218
|
closeSync2(fd);
|
|
4483
4219
|
}
|
|
4484
4220
|
}
|
|
4485
|
-
function
|
|
4221
|
+
function described(path, until, describe2) {
|
|
4486
4222
|
const stamp = statSync(path);
|
|
4487
4223
|
const saved = metadata.get(path);
|
|
4488
|
-
if (saved && unchanged(saved.stamp, stamp)) return saved.
|
|
4224
|
+
if (saved && unchanged(saved.stamp, stamp)) return saved.value;
|
|
4489
4225
|
let head = readRange(path, 0, Math.min(stamp.size, SAMPLE_BYTES));
|
|
4490
4226
|
if (head.length < stamp.size && head.at(-1) !== 10) {
|
|
4491
4227
|
const chunks = [head];
|
|
@@ -4508,9 +4244,9 @@ function sample(path, until) {
|
|
|
4508
4244
|
);
|
|
4509
4245
|
if (start === head.length || tail.some(until)) break;
|
|
4510
4246
|
}
|
|
4511
|
-
const
|
|
4512
|
-
metadata.set(path, { stamp,
|
|
4513
|
-
return
|
|
4247
|
+
const value2 = describe2([...records(head), ...tail], stamp);
|
|
4248
|
+
metadata.set(path, { stamp, value: value2 });
|
|
4249
|
+
return value2;
|
|
4514
4250
|
}
|
|
4515
4251
|
var windowed = (path, messages5) => journals.get(path)?.start ? [EARLIER, ...messages5] : messages5;
|
|
4516
4252
|
function journal(path, latest = false) {
|
|
@@ -4552,7 +4288,7 @@ function journal(path, latest = false) {
|
|
|
4552
4288
|
return tail ? [...saved.rows, tail] : saved.rows;
|
|
4553
4289
|
}
|
|
4554
4290
|
function files(root, scan, depth = Infinity) {
|
|
4555
|
-
if (!
|
|
4291
|
+
if (!existsSync2(root)) {
|
|
4556
4292
|
watchParent(root, scan);
|
|
4557
4293
|
return [];
|
|
4558
4294
|
}
|
|
@@ -4569,8 +4305,8 @@ function files(root, scan, depth = Infinity) {
|
|
|
4569
4305
|
}
|
|
4570
4306
|
}
|
|
4571
4307
|
function watchParent(path, scan) {
|
|
4572
|
-
while (!
|
|
4573
|
-
if (
|
|
4308
|
+
while (!existsSync2(path) && dirname2(path) !== path) path = dirname2(path);
|
|
4309
|
+
if (existsSync2(path)) scan.paths.add(path);
|
|
4574
4310
|
}
|
|
4575
4311
|
function canonical(cwd) {
|
|
4576
4312
|
if (typeof cwd !== "string" || !isAbsolute(cwd)) return void 0;
|
|
@@ -4731,26 +4467,31 @@ function claudeConversation(conversation, rows) {
|
|
|
4731
4467
|
function claude(root, scan) {
|
|
4732
4468
|
for (const path of files(join2(root, "projects"), scan, 1).filter((file) => file.endsWith(".jsonl"))) {
|
|
4733
4469
|
try {
|
|
4734
|
-
const
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
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) {
|
|
4738
4489
|
scan.warnings.push(`Unrecognized Claude Code session: ${path}`);
|
|
4739
4490
|
continue;
|
|
4740
4491
|
}
|
|
4741
|
-
const named = latest.find((row) => row.type === "custom-title" || row.type === "ai-title");
|
|
4742
|
-
const first = rows.find(
|
|
4743
|
-
(row) => row.type === "user" && !row.isMeta && !row.isSidechain && !injected(row.message?.content)
|
|
4744
|
-
);
|
|
4745
4492
|
scan.sessions.push({
|
|
4746
4493
|
harness: "claude",
|
|
4747
|
-
|
|
4748
|
-
cwd: canonical(owner.cwd),
|
|
4749
|
-
title: title(
|
|
4750
|
-
named?.customTitle ?? named?.aiTitle ?? text(first?.message?.content),
|
|
4751
|
-
"Claude Code thread"
|
|
4752
|
-
),
|
|
4753
|
-
updatedAt: date(latest.find(claudeMessage)?.timestamp, statSync(path).mtimeMs),
|
|
4494
|
+
...session,
|
|
4754
4495
|
files: [path],
|
|
4755
4496
|
read: () => claudeMessages(path)
|
|
4756
4497
|
});
|
|
@@ -4822,7 +4563,7 @@ function codexMessages(path) {
|
|
|
4822
4563
|
function codex(root, scan) {
|
|
4823
4564
|
const index = join2(root, "session_index.jsonl");
|
|
4824
4565
|
const names = /* @__PURE__ */ new Map();
|
|
4825
|
-
if (
|
|
4566
|
+
if (existsSync2(index)) {
|
|
4826
4567
|
scan.paths.add(root);
|
|
4827
4568
|
try {
|
|
4828
4569
|
for (const row of journal(index)) if (typeof row.id === "string") names.set(row.id, row);
|
|
@@ -4831,34 +4572,41 @@ function codex(root, scan) {
|
|
|
4831
4572
|
}
|
|
4832
4573
|
}
|
|
4833
4574
|
const titles = codexTitles(root);
|
|
4575
|
+
const indexed = existsSync2(index);
|
|
4834
4576
|
for (const path of [
|
|
4835
4577
|
...files(join2(root, "sessions"), scan),
|
|
4836
4578
|
...files(join2(root, "archived_sessions"), scan)
|
|
4837
4579
|
].filter((file) => file.endsWith(".jsonl"))) {
|
|
4838
4580
|
try {
|
|
4839
|
-
const
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
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) {
|
|
4844
4598
|
scan.warnings.push(`Unrecognized Codex session: ${path}`);
|
|
4845
4599
|
continue;
|
|
4846
4600
|
}
|
|
4847
|
-
const
|
|
4848
|
-
const first = rows.find(
|
|
4849
|
-
(row) => row.type === "response_item" && row.payload?.type === "message" && row.payload.role === "user" && !injected(row.payload.content)
|
|
4850
|
-
);
|
|
4601
|
+
const { asked, ...found } = session;
|
|
4851
4602
|
scan.sessions.push({
|
|
4852
4603
|
harness: "codex",
|
|
4853
|
-
|
|
4854
|
-
cwd,
|
|
4604
|
+
...found,
|
|
4855
4605
|
title: title(
|
|
4856
|
-
|
|
4606
|
+
names.get(found.id)?.thread_name ?? titles.get(found.id) ?? asked,
|
|
4857
4607
|
"Codex thread"
|
|
4858
4608
|
),
|
|
4859
|
-
|
|
4860
|
-
updatedAt: date(latest.find(codexItem)?.timestamp, statSync(path).mtimeMs),
|
|
4861
|
-
files: [path, ...existsSync3(index) ? [index] : []],
|
|
4609
|
+
files: [path, ...indexed ? [index] : []],
|
|
4862
4610
|
read: () => codexMessages(path)
|
|
4863
4611
|
});
|
|
4864
4612
|
} catch {
|
|
@@ -4925,17 +4673,22 @@ function databaseMessages(path, session) {
|
|
|
4925
4673
|
}
|
|
4926
4674
|
}
|
|
4927
4675
|
function codexSubagent(row) {
|
|
4928
|
-
const
|
|
4929
|
-
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() : "";
|
|
4930
4678
|
return [row.agent_nickname, task?.replace(/_/g, " ")].filter(Boolean).join(" \xB7 ");
|
|
4931
4679
|
}
|
|
4680
|
+
var codexState;
|
|
4932
4681
|
function codexTitles(root) {
|
|
4933
4682
|
const titles = /* @__PURE__ */ new Map();
|
|
4934
|
-
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];
|
|
4935
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 };
|
|
4936
4689
|
let db;
|
|
4937
4690
|
try {
|
|
4938
|
-
db = openDatabase(
|
|
4691
|
+
db = openDatabase(path);
|
|
4939
4692
|
for (const row of db.prepare("SELECT id, title, first_user_message, source, agent_nickname FROM threads").all()) {
|
|
4940
4693
|
const name = [row.title, row.first_user_message].find(
|
|
4941
4694
|
(value2) => typeof value2 === "string" && !!value2.trim() && !injected(value2)
|
|
@@ -4949,7 +4702,7 @@ function codexTitles(root) {
|
|
|
4949
4702
|
return titles;
|
|
4950
4703
|
}
|
|
4951
4704
|
function openCodeDatabase(path, scan) {
|
|
4952
|
-
if (!
|
|
4705
|
+
if (!existsSync2(path)) {
|
|
4953
4706
|
watchParent(path, scan);
|
|
4954
4707
|
return;
|
|
4955
4708
|
}
|
|
@@ -5011,7 +4764,7 @@ function legacyMessages(root, session) {
|
|
|
5011
4764
|
}
|
|
5012
4765
|
function openCodeLegacy(root, scan) {
|
|
5013
4766
|
const parts2 = join2(root, "part");
|
|
5014
|
-
if (
|
|
4767
|
+
if (existsSync2(parts2)) files(parts2, scan, 1);
|
|
5015
4768
|
for (const path of files(join2(root, "session"), scan).filter((file) => file.endsWith(".json"))) {
|
|
5016
4769
|
try {
|
|
5017
4770
|
const row = jsonFile(path);
|
|
@@ -5064,7 +4817,22 @@ function scanHistories() {
|
|
|
5064
4817
|
}
|
|
5065
4818
|
|
|
5066
4819
|
// src/discovery.ts
|
|
5067
|
-
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
|
+
};
|
|
5068
4836
|
var agentThread = (harness, session, secret) => opaqueId(`agent\0${harness}\0${session}`, secret);
|
|
5069
4837
|
var zedHost = (value2) => typeof value2 === "string" ? value2 : value2 && typeof value2 === "object" ? String(Object.values(value2)[0] ?? "") : "";
|
|
5070
4838
|
function vscodeHost(authority) {
|
|
@@ -5150,13 +4918,11 @@ function contentText(content) {
|
|
|
5150
4918
|
return typeof part.text === "string" ? part.text : typeof part.Text === "string" ? part.Text : typeof part.value === "string" ? part.value : contentText(part.content);
|
|
5151
4919
|
}
|
|
5152
4920
|
var Discovery = class {
|
|
5153
|
-
constructor(deviceId,
|
|
4921
|
+
constructor(deviceId, secret) {
|
|
5154
4922
|
this.deviceId = deviceId;
|
|
5155
|
-
this.capabilities = capabilities;
|
|
5156
4923
|
this.secret = secret;
|
|
5157
4924
|
}
|
|
5158
4925
|
deviceId;
|
|
5159
|
-
capabilities;
|
|
5160
4926
|
secret;
|
|
5161
4927
|
projects = /* @__PURE__ */ new Map();
|
|
5162
4928
|
locations = /* @__PURE__ */ new Map();
|
|
@@ -5192,7 +4958,7 @@ var Discovery = class {
|
|
|
5192
4958
|
editor: "cli",
|
|
5193
4959
|
status: "unknown",
|
|
5194
4960
|
updatedAt: session.updatedAt,
|
|
5195
|
-
capabilities: { ...
|
|
4961
|
+
capabilities: { ...READ_ONLY },
|
|
5196
4962
|
agent: { harness: session.harness, sessionId: session.id }
|
|
5197
4963
|
};
|
|
5198
4964
|
project.threads.push(thread);
|
|
@@ -5216,7 +4982,7 @@ var Discovery = class {
|
|
|
5216
4982
|
const parent = dirname3(project.path);
|
|
5217
4983
|
counts.set(parent, (counts.get(parent) ?? 0) + 1);
|
|
5218
4984
|
}
|
|
5219
|
-
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);
|
|
5220
4986
|
return folders.length ? folders : [homedir3()];
|
|
5221
4987
|
}
|
|
5222
4988
|
observe(session) {
|
|
@@ -5253,7 +5019,7 @@ var Discovery = class {
|
|
|
5253
5019
|
editor: "acp",
|
|
5254
5020
|
status: "idle",
|
|
5255
5021
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5256
|
-
capabilities:
|
|
5022
|
+
capabilities: READ_ONLY
|
|
5257
5023
|
};
|
|
5258
5024
|
project.threads.push(thread);
|
|
5259
5025
|
this.locations.set(id, { editor: "acp", path: "", session });
|
|
@@ -5281,11 +5047,11 @@ var Discovery = class {
|
|
|
5281
5047
|
zed(root) {
|
|
5282
5048
|
const database = join3(root, "db");
|
|
5283
5049
|
const store = join3(root, "threads/threads.db");
|
|
5284
|
-
if (!
|
|
5050
|
+
if (!existsSync3(database)) return;
|
|
5285
5051
|
this.paths.add(database);
|
|
5286
5052
|
for (const entry of readdirSync2(database)) {
|
|
5287
5053
|
const path = join3(database, entry, "db.sqlite");
|
|
5288
|
-
if (!
|
|
5054
|
+
if (!existsSync3(path)) continue;
|
|
5289
5055
|
this.paths.add(join3(database, entry));
|
|
5290
5056
|
try {
|
|
5291
5057
|
if (!sqlite(
|
|
@@ -5294,7 +5060,7 @@ var Discovery = class {
|
|
|
5294
5060
|
).length)
|
|
5295
5061
|
continue;
|
|
5296
5062
|
const stamps = new Map(
|
|
5297
|
-
|
|
5063
|
+
existsSync3(store) ? sqlite(store, "SELECT id, updated_at FROM threads").map((row) => [
|
|
5298
5064
|
row.id,
|
|
5299
5065
|
row.updated_at
|
|
5300
5066
|
]) : []
|
|
@@ -5327,7 +5093,7 @@ var Discovery = class {
|
|
|
5327
5093
|
editor: "zed",
|
|
5328
5094
|
status: "unknown",
|
|
5329
5095
|
updatedAt,
|
|
5330
|
-
capabilities:
|
|
5096
|
+
capabilities: READ_ONLY,
|
|
5331
5097
|
...harness ? { agent: { harness, sessionId: session } } : {}
|
|
5332
5098
|
};
|
|
5333
5099
|
project.threads.push(thread);
|
|
@@ -5342,17 +5108,17 @@ var Discovery = class {
|
|
|
5342
5108
|
}
|
|
5343
5109
|
}
|
|
5344
5110
|
const threads = join3(root, "threads");
|
|
5345
|
-
if (
|
|
5111
|
+
if (existsSync3(threads)) this.paths.add(threads);
|
|
5346
5112
|
}
|
|
5347
5113
|
vscode(root) {
|
|
5348
5114
|
const workspaces = join3(root, "workspaceStorage");
|
|
5349
|
-
if (!
|
|
5115
|
+
if (!existsSync3(workspaces)) return;
|
|
5350
5116
|
this.paths.add(workspaces);
|
|
5351
5117
|
for (const entry of readdirSync2(workspaces)) {
|
|
5352
5118
|
const directory2 = join3(workspaces, entry);
|
|
5353
5119
|
const workspace = join3(directory2, "workspace.json");
|
|
5354
5120
|
const sessions = join3(directory2, "chatSessions");
|
|
5355
|
-
if (!
|
|
5121
|
+
if (!existsSync3(workspace)) continue;
|
|
5356
5122
|
this.paths.add(directory2);
|
|
5357
5123
|
try {
|
|
5358
5124
|
const data = JSON.parse(readFileSync2(workspace, "utf8"));
|
|
@@ -5361,7 +5127,7 @@ var Discovery = class {
|
|
|
5361
5127
|
const path = decodeURIComponent(uri.pathname).replace(/^\/([A-Za-z]:\/)/, "$1");
|
|
5362
5128
|
const host = uri.protocol === "vscode-remote:" ? vscodeHost(uri.host) : "";
|
|
5363
5129
|
const project = this.project(path, host ? { host, path } : void 0);
|
|
5364
|
-
if (!
|
|
5130
|
+
if (!existsSync3(sessions)) continue;
|
|
5365
5131
|
this.paths.add(sessions);
|
|
5366
5132
|
for (const filename of readdirSync2(sessions).filter((name) => /\.jsonl?$/.test(name))) {
|
|
5367
5133
|
const file = join3(sessions, filename);
|
|
@@ -5373,7 +5139,7 @@ var Discovery = class {
|
|
|
5373
5139
|
editor: "vscode",
|
|
5374
5140
|
status: "unknown",
|
|
5375
5141
|
updatedAt: new Date(state.lastMessageDate ?? state.creationDate ?? 0).toISOString(),
|
|
5376
|
-
capabilities:
|
|
5142
|
+
capabilities: READ_ONLY
|
|
5377
5143
|
});
|
|
5378
5144
|
this.locations.set(id, { editor: "vscode", path: file });
|
|
5379
5145
|
}
|
|
@@ -5399,16 +5165,16 @@ var Discovery = class {
|
|
|
5399
5165
|
}
|
|
5400
5166
|
]).filter((message) => message.text);
|
|
5401
5167
|
}
|
|
5402
|
-
if (!
|
|
5168
|
+
if (!existsSync3(location2.path))
|
|
5403
5169
|
throw new Error(
|
|
5404
|
-
"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."
|
|
5405
5171
|
);
|
|
5406
5172
|
const db = new DatabaseSync2(location2.path, { readOnly: true });
|
|
5407
5173
|
try {
|
|
5408
5174
|
const row = db.prepare("SELECT data_type, data FROM threads WHERE id = ?").get(location2.session);
|
|
5409
5175
|
if (!row)
|
|
5410
5176
|
throw new Error(
|
|
5411
|
-
"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."
|
|
5412
5178
|
);
|
|
5413
5179
|
const data = row.data_type === "zstd" ? zstdDecompressSync(row.data) : row.data;
|
|
5414
5180
|
const state = JSON.parse(Buffer.from(data).toString("utf8"));
|
|
@@ -10636,20 +10402,20 @@ var deflate$2 = (strm, flush) => {
|
|
|
10636
10402
|
let beg = s.pending;
|
|
10637
10403
|
let left = (s.gzhead.extra.length & 65535) - s.gzindex;
|
|
10638
10404
|
while (s.pending + left > s.pending_buf_size) {
|
|
10639
|
-
let
|
|
10640
|
-
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);
|
|
10641
10407
|
s.pending = s.pending_buf_size;
|
|
10642
10408
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
10643
10409
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
10644
10410
|
}
|
|
10645
|
-
s.gzindex +=
|
|
10411
|
+
s.gzindex += copy2;
|
|
10646
10412
|
flush_pending(strm);
|
|
10647
10413
|
if (s.pending !== 0) {
|
|
10648
10414
|
s.last_flush = -1;
|
|
10649
10415
|
return Z_OK$3;
|
|
10650
10416
|
}
|
|
10651
10417
|
beg = 0;
|
|
10652
|
-
left -=
|
|
10418
|
+
left -= copy2;
|
|
10653
10419
|
}
|
|
10654
10420
|
let gzhead_extra = new Uint8Array(s.gzhead.extra);
|
|
10655
10421
|
s.pending_buf.set(gzhead_extra.subarray(s.gzindex, s.gzindex + left), s.pending);
|
|
@@ -11959,7 +11725,7 @@ var fixedtables = (state) => {
|
|
|
11959
11725
|
state.distcode = distfix;
|
|
11960
11726
|
state.distbits = 5;
|
|
11961
11727
|
};
|
|
11962
|
-
var updatewindow = (strm, src, end,
|
|
11728
|
+
var updatewindow = (strm, src, end, copy2) => {
|
|
11963
11729
|
let dist;
|
|
11964
11730
|
const state = strm.state;
|
|
11965
11731
|
if (state.window === null) {
|
|
@@ -11970,20 +11736,20 @@ var updatewindow = (strm, src, end, copy) => {
|
|
|
11970
11736
|
state.wnext = 0;
|
|
11971
11737
|
state.whave = 0;
|
|
11972
11738
|
}
|
|
11973
|
-
if (
|
|
11739
|
+
if (copy2 >= state.wsize) {
|
|
11974
11740
|
state.window.set(src.subarray(end - state.wsize, end), 0);
|
|
11975
11741
|
state.wnext = 0;
|
|
11976
11742
|
state.whave = state.wsize;
|
|
11977
11743
|
} else {
|
|
11978
11744
|
dist = state.wsize - state.wnext;
|
|
11979
|
-
if (dist >
|
|
11980
|
-
dist =
|
|
11981
|
-
}
|
|
11982
|
-
state.window.set(src.subarray(end -
|
|
11983
|
-
|
|
11984
|
-
if (
|
|
11985
|
-
state.window.set(src.subarray(end -
|
|
11986
|
-
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;
|
|
11987
11753
|
state.whave = state.wsize;
|
|
11988
11754
|
} else {
|
|
11989
11755
|
state.wnext += dist;
|
|
@@ -12006,7 +11772,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
12006
11772
|
let hold;
|
|
12007
11773
|
let bits;
|
|
12008
11774
|
let _in, _out;
|
|
12009
|
-
let
|
|
11775
|
+
let copy2;
|
|
12010
11776
|
let from;
|
|
12011
11777
|
let from_source;
|
|
12012
11778
|
let here = 0;
|
|
@@ -12205,11 +11971,11 @@ var inflate$2 = (strm, flush) => {
|
|
|
12205
11971
|
/* falls through */
|
|
12206
11972
|
case EXTRA:
|
|
12207
11973
|
if (state.flags & 1024) {
|
|
12208
|
-
|
|
12209
|
-
if (
|
|
12210
|
-
|
|
11974
|
+
copy2 = state.length;
|
|
11975
|
+
if (copy2 > have) {
|
|
11976
|
+
copy2 = have;
|
|
12211
11977
|
}
|
|
12212
|
-
if (
|
|
11978
|
+
if (copy2) {
|
|
12213
11979
|
if (state.head) {
|
|
12214
11980
|
len = state.head.extra_len - state.length;
|
|
12215
11981
|
if (!state.head.extra) {
|
|
@@ -12220,18 +11986,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12220
11986
|
next,
|
|
12221
11987
|
// extra field is limited to 65536 bytes
|
|
12222
11988
|
// - no need for additional size check
|
|
12223
|
-
next +
|
|
11989
|
+
next + copy2
|
|
12224
11990
|
),
|
|
12225
11991
|
/*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
|
|
12226
11992
|
len
|
|
12227
11993
|
);
|
|
12228
11994
|
}
|
|
12229
11995
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12230
|
-
state.check = crc32_1(state.check, input,
|
|
11996
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12231
11997
|
}
|
|
12232
|
-
have -=
|
|
12233
|
-
next +=
|
|
12234
|
-
state.length -=
|
|
11998
|
+
have -= copy2;
|
|
11999
|
+
next += copy2;
|
|
12000
|
+
state.length -= copy2;
|
|
12235
12001
|
}
|
|
12236
12002
|
if (state.length) {
|
|
12237
12003
|
break inf_leave;
|
|
@@ -12245,18 +12011,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12245
12011
|
if (have === 0) {
|
|
12246
12012
|
break inf_leave;
|
|
12247
12013
|
}
|
|
12248
|
-
|
|
12014
|
+
copy2 = 0;
|
|
12249
12015
|
do {
|
|
12250
|
-
len = input[next +
|
|
12016
|
+
len = input[next + copy2++];
|
|
12251
12017
|
if (state.head && len && state.length < 65536) {
|
|
12252
12018
|
state.head.name += String.fromCharCode(len);
|
|
12253
12019
|
}
|
|
12254
|
-
} while (len &&
|
|
12020
|
+
} while (len && copy2 < have);
|
|
12255
12021
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12256
|
-
state.check = crc32_1(state.check, input,
|
|
12022
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12257
12023
|
}
|
|
12258
|
-
have -=
|
|
12259
|
-
next +=
|
|
12024
|
+
have -= copy2;
|
|
12025
|
+
next += copy2;
|
|
12260
12026
|
if (len) {
|
|
12261
12027
|
break inf_leave;
|
|
12262
12028
|
}
|
|
@@ -12271,18 +12037,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12271
12037
|
if (have === 0) {
|
|
12272
12038
|
break inf_leave;
|
|
12273
12039
|
}
|
|
12274
|
-
|
|
12040
|
+
copy2 = 0;
|
|
12275
12041
|
do {
|
|
12276
|
-
len = input[next +
|
|
12042
|
+
len = input[next + copy2++];
|
|
12277
12043
|
if (state.head && len && state.length < 65536) {
|
|
12278
12044
|
state.head.comment += String.fromCharCode(len);
|
|
12279
12045
|
}
|
|
12280
|
-
} while (len &&
|
|
12046
|
+
} while (len && copy2 < have);
|
|
12281
12047
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12282
|
-
state.check = crc32_1(state.check, input,
|
|
12048
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12283
12049
|
}
|
|
12284
|
-
have -=
|
|
12285
|
-
next +=
|
|
12050
|
+
have -= copy2;
|
|
12051
|
+
next += copy2;
|
|
12286
12052
|
if (len) {
|
|
12287
12053
|
break inf_leave;
|
|
12288
12054
|
}
|
|
@@ -12417,23 +12183,23 @@ var inflate$2 = (strm, flush) => {
|
|
|
12417
12183
|
state.mode = COPY;
|
|
12418
12184
|
/* falls through */
|
|
12419
12185
|
case COPY:
|
|
12420
|
-
|
|
12421
|
-
if (
|
|
12422
|
-
if (
|
|
12423
|
-
|
|
12186
|
+
copy2 = state.length;
|
|
12187
|
+
if (copy2) {
|
|
12188
|
+
if (copy2 > have) {
|
|
12189
|
+
copy2 = have;
|
|
12424
12190
|
}
|
|
12425
|
-
if (
|
|
12426
|
-
|
|
12191
|
+
if (copy2 > left) {
|
|
12192
|
+
copy2 = left;
|
|
12427
12193
|
}
|
|
12428
|
-
if (
|
|
12194
|
+
if (copy2 === 0) {
|
|
12429
12195
|
break inf_leave;
|
|
12430
12196
|
}
|
|
12431
|
-
output.set(input.subarray(next, next +
|
|
12432
|
-
have -=
|
|
12433
|
-
next +=
|
|
12434
|
-
left -=
|
|
12435
|
-
put +=
|
|
12436
|
-
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;
|
|
12437
12203
|
break;
|
|
12438
12204
|
}
|
|
12439
12205
|
state.mode = TYPE;
|
|
@@ -12534,7 +12300,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
12534
12300
|
break;
|
|
12535
12301
|
}
|
|
12536
12302
|
len = state.lens[state.have - 1];
|
|
12537
|
-
|
|
12303
|
+
copy2 = 3 + (hold & 3);
|
|
12538
12304
|
hold >>>= 2;
|
|
12539
12305
|
bits -= 2;
|
|
12540
12306
|
} else if (here_val === 17) {
|
|
@@ -12550,7 +12316,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
12550
12316
|
hold >>>= here_bits;
|
|
12551
12317
|
bits -= here_bits;
|
|
12552
12318
|
len = 0;
|
|
12553
|
-
|
|
12319
|
+
copy2 = 3 + (hold & 7);
|
|
12554
12320
|
hold >>>= 3;
|
|
12555
12321
|
bits -= 3;
|
|
12556
12322
|
} else {
|
|
@@ -12566,16 +12332,16 @@ var inflate$2 = (strm, flush) => {
|
|
|
12566
12332
|
hold >>>= here_bits;
|
|
12567
12333
|
bits -= here_bits;
|
|
12568
12334
|
len = 0;
|
|
12569
|
-
|
|
12335
|
+
copy2 = 11 + (hold & 127);
|
|
12570
12336
|
hold >>>= 7;
|
|
12571
12337
|
bits -= 7;
|
|
12572
12338
|
}
|
|
12573
|
-
if (state.have +
|
|
12339
|
+
if (state.have + copy2 > state.nlen + state.ndist) {
|
|
12574
12340
|
strm.msg = "invalid bit length repeat";
|
|
12575
12341
|
state.mode = BAD;
|
|
12576
12342
|
break;
|
|
12577
12343
|
}
|
|
12578
|
-
while (
|
|
12344
|
+
while (copy2--) {
|
|
12579
12345
|
state.lens[state.have++] = len;
|
|
12580
12346
|
}
|
|
12581
12347
|
}
|
|
@@ -12794,39 +12560,39 @@ var inflate$2 = (strm, flush) => {
|
|
|
12794
12560
|
if (left === 0) {
|
|
12795
12561
|
break inf_leave;
|
|
12796
12562
|
}
|
|
12797
|
-
|
|
12798
|
-
if (state.offset >
|
|
12799
|
-
|
|
12800
|
-
if (
|
|
12563
|
+
copy2 = _out - left;
|
|
12564
|
+
if (state.offset > copy2) {
|
|
12565
|
+
copy2 = state.offset - copy2;
|
|
12566
|
+
if (copy2 > state.whave) {
|
|
12801
12567
|
if (state.sane) {
|
|
12802
12568
|
strm.msg = "invalid distance too far back";
|
|
12803
12569
|
state.mode = BAD;
|
|
12804
12570
|
break;
|
|
12805
12571
|
}
|
|
12806
12572
|
}
|
|
12807
|
-
if (
|
|
12808
|
-
|
|
12809
|
-
from = state.wsize -
|
|
12573
|
+
if (copy2 > state.wnext) {
|
|
12574
|
+
copy2 -= state.wnext;
|
|
12575
|
+
from = state.wsize - copy2;
|
|
12810
12576
|
} else {
|
|
12811
|
-
from = state.wnext -
|
|
12577
|
+
from = state.wnext - copy2;
|
|
12812
12578
|
}
|
|
12813
|
-
if (
|
|
12814
|
-
|
|
12579
|
+
if (copy2 > state.length) {
|
|
12580
|
+
copy2 = state.length;
|
|
12815
12581
|
}
|
|
12816
12582
|
from_source = state.window;
|
|
12817
12583
|
} else {
|
|
12818
12584
|
from_source = output;
|
|
12819
12585
|
from = put - state.offset;
|
|
12820
|
-
|
|
12586
|
+
copy2 = state.length;
|
|
12821
12587
|
}
|
|
12822
|
-
if (
|
|
12823
|
-
|
|
12588
|
+
if (copy2 > left) {
|
|
12589
|
+
copy2 = left;
|
|
12824
12590
|
}
|
|
12825
|
-
left -=
|
|
12826
|
-
state.length -=
|
|
12591
|
+
left -= copy2;
|
|
12592
|
+
state.length -= copy2;
|
|
12827
12593
|
do {
|
|
12828
12594
|
output[put++] = from_source[from++];
|
|
12829
|
-
} while (--
|
|
12595
|
+
} while (--copy2);
|
|
12830
12596
|
if (state.length === 0) {
|
|
12831
12597
|
state.mode = LEN;
|
|
12832
12598
|
}
|
|
@@ -14717,12 +14483,12 @@ var Model = class _Model extends import_index2.default {
|
|
|
14717
14483
|
});
|
|
14718
14484
|
return value2;
|
|
14719
14485
|
} catch {
|
|
14720
|
-
const
|
|
14721
|
-
Object.defineProperty(
|
|
14486
|
+
const copy2 = { ...value2 };
|
|
14487
|
+
Object.defineProperty(copy2, SYM_EXPANDED_REF, {
|
|
14722
14488
|
enumerable: true,
|
|
14723
14489
|
value: true
|
|
14724
14490
|
});
|
|
14725
|
-
return
|
|
14491
|
+
return copy2;
|
|
14726
14492
|
}
|
|
14727
14493
|
}
|
|
14728
14494
|
/**
|
|
@@ -16410,6 +16176,23 @@ function sealHistory(key, routing, message, random) {
|
|
|
16410
16176
|
// src/history.ts
|
|
16411
16177
|
var HISTORY_PATH = "/dashup/history";
|
|
16412
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
|
+
}
|
|
16413
16196
|
async function upload(config2, batch) {
|
|
16414
16197
|
if (batch.records.length > HISTORY_BATCH_ITEMS || Buffer.byteLength(JSON.stringify(batch)) > HISTORY_BATCH_BYTES)
|
|
16415
16198
|
throw new Error("The encrypted history upload exceeds the batch limit");
|
|
@@ -16424,15 +16207,10 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16424
16207
|
const local = config2.pairs[pair.id];
|
|
16425
16208
|
const generation = ++local.sequence;
|
|
16426
16209
|
saveConfig(config2);
|
|
16427
|
-
const
|
|
16428
|
-
const named = (id) => opaque(["history-message", project, thread.id, id]);
|
|
16429
|
-
const all = splitHistoryMessages(messages5, named);
|
|
16210
|
+
const all = historyMessages(local.key, project, thread.id, messages5);
|
|
16430
16211
|
let start = Math.max(0, all.length - HISTORY_KEPT);
|
|
16431
16212
|
while (start < all.length && (all[start].part ?? 0) > 0) start++;
|
|
16432
|
-
const fragments = start ? [
|
|
16433
|
-
...splitHistoryMessages([EARLIER], named),
|
|
16434
|
-
...all.slice(start)
|
|
16435
|
-
] : all;
|
|
16213
|
+
const fragments = start ? [...historyMessages(local.key, project, thread.id, [EARLIER]), ...all.slice(start)] : all;
|
|
16436
16214
|
const summary = { generation, total: fragments.length, updatedAt: thread.updatedAt };
|
|
16437
16215
|
const batch = {
|
|
16438
16216
|
pairing: pair.id,
|
|
@@ -16448,7 +16226,7 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16448
16226
|
const record = sealHistory(
|
|
16449
16227
|
local.key,
|
|
16450
16228
|
{
|
|
16451
|
-
id: opaque(["history-record", project, thread.id, generation, position]),
|
|
16229
|
+
id: opaque(local.key, ["history-record", project, thread.id, generation, position]),
|
|
16452
16230
|
pairing: pair.id,
|
|
16453
16231
|
sender: pair.device,
|
|
16454
16232
|
recipient: pair.mobile,
|
|
@@ -16477,16 +16255,16 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16477
16255
|
}
|
|
16478
16256
|
|
|
16479
16257
|
// src/harness/live.ts
|
|
16480
|
-
import { promisify as
|
|
16481
|
-
import { execFile as
|
|
16258
|
+
import { promisify as promisify5 } from "node:util";
|
|
16259
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
16482
16260
|
|
|
16483
16261
|
// src/harness/codex.ts
|
|
16484
16262
|
import WebSocket2 from "ws";
|
|
16485
16263
|
import { homedir as homedir4 } from "node:os";
|
|
16486
16264
|
import { connect } from "node:net";
|
|
16487
|
-
import { promisify as
|
|
16265
|
+
import { promisify as promisify2 } from "node:util";
|
|
16488
16266
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
16489
|
-
import { execFile as
|
|
16267
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
16490
16268
|
import { join as join4, isAbsolute as isAbsolute2 } from "node:path";
|
|
16491
16269
|
import { stat, readdir, readFile } from "node:fs/promises";
|
|
16492
16270
|
var REQUEST_MS = 4e3;
|
|
@@ -16510,7 +16288,7 @@ function approval(id, method, params) {
|
|
|
16510
16288
|
const oversized = Buffer.byteLength(detail) > MAX_PREVIEW;
|
|
16511
16289
|
const unavailable = !command2 || params.kind !== void 0 && params.kind !== "command" || typeof params.command !== "string" || !params.command.trim() || oversized;
|
|
16512
16290
|
if (oversized) detail = Buffer.from(detail).subarray(0, MAX_PREVIEW).toString("utf8");
|
|
16513
|
-
if (unavailable) detail = [detail,
|
|
16291
|
+
if (unavailable) detail = [detail, NO_PREVIEW].filter(Boolean).join("\n\n");
|
|
16514
16292
|
return {
|
|
16515
16293
|
id,
|
|
16516
16294
|
title: command2 ? "Allow command?" : "Allow file changes?",
|
|
@@ -16806,7 +16584,7 @@ var Codex = class {
|
|
|
16806
16584
|
// Codex's own daemon runs the threads Dashup starts or resumes, so they outlive this connector.
|
|
16807
16585
|
async start(cwd, text3, saved) {
|
|
16808
16586
|
const path = process.env.DASHUP_CODEX_SOCKET || join4(codexHome(), ...SOCKET_PARTS);
|
|
16809
|
-
await
|
|
16587
|
+
await promisify2(execFile2)("codex", ["app-server", "daemon", "start"], {
|
|
16810
16588
|
timeout: 6e4,
|
|
16811
16589
|
shell: process.platform === "win32"
|
|
16812
16590
|
});
|
|
@@ -16849,8 +16627,8 @@ var Codex = class {
|
|
|
16849
16627
|
// src/harness/headless.ts
|
|
16850
16628
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
16851
16629
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
16852
|
-
import { createInterface as
|
|
16853
|
-
import { spawn as
|
|
16630
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
16631
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
16854
16632
|
var CHOICES = [
|
|
16855
16633
|
{ id: "accept", label: "Allow once" },
|
|
16856
16634
|
{ id: "decline", label: "Reject" }
|
|
@@ -16973,7 +16751,7 @@ var Session = class {
|
|
|
16973
16751
|
this.changed();
|
|
16974
16752
|
return Promise.resolve();
|
|
16975
16753
|
}
|
|
16976
|
-
const turn = this.turn =
|
|
16754
|
+
const turn = this.turn = spawn2(
|
|
16977
16755
|
"claude",
|
|
16978
16756
|
[
|
|
16979
16757
|
"-p",
|
|
@@ -16996,7 +16774,7 @@ var Session = class {
|
|
|
16996
16774
|
errors = (errors + String(chunk)).slice(-2e3);
|
|
16997
16775
|
});
|
|
16998
16776
|
const started = new Promise((resolve3, reject) => {
|
|
16999
|
-
|
|
16777
|
+
createInterface2({ input: turn.stdout }).on("line", (line) => {
|
|
17000
16778
|
if (this.receive(line)) resolve3();
|
|
17001
16779
|
});
|
|
17002
16780
|
const end = (reason) => {
|
|
@@ -17028,7 +16806,7 @@ var Session = class {
|
|
|
17028
16806
|
else if (message.type === "assistant") {
|
|
17029
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");
|
|
17030
16808
|
if (!text3) return false;
|
|
17031
|
-
this.messages.push({ id: String(message.
|
|
16809
|
+
this.messages.push({ id: String(message.uuid ?? randomUUID3()), role: "assistant", text: text3 });
|
|
17032
16810
|
} else if (message.type === "control_request" && message.request?.subtype === "can_use_tool") {
|
|
17033
16811
|
const id = randomUUID3();
|
|
17034
16812
|
this.approvals.set(id, {
|
|
@@ -17250,9 +17028,9 @@ var Claude = class {
|
|
|
17250
17028
|
import Socket3 from "ws";
|
|
17251
17029
|
import { homedir as homedir5 } from "node:os";
|
|
17252
17030
|
import { connect as connect3 } from "node:net";
|
|
17253
|
-
import { promisify as
|
|
17254
|
-
import { fileURLToPath
|
|
17255
|
-
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";
|
|
17256
17034
|
import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
|
|
17257
17035
|
import { stat as stat2, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
17258
17036
|
import { join as join6, dirname as dirname4, basename as basename2, isAbsolute as isAbsolute3 } from "node:path";
|
|
@@ -17265,7 +17043,7 @@ var MAX_METADATA = 64 * 1024;
|
|
|
17265
17043
|
var MAX_HOSTS = 16;
|
|
17266
17044
|
var MAX_SESSIONS3 = 100;
|
|
17267
17045
|
var ROOT_CHANNEL = "ahp-root://";
|
|
17268
|
-
var
|
|
17046
|
+
var run = promisify3(execFile3);
|
|
17269
17047
|
var CONFIRMATION_CHOICES = [
|
|
17270
17048
|
{ id: "approve", label: "Allow once", kind: "approve" },
|
|
17271
17049
|
{ id: "deny", label: "Reject", kind: "deny" }
|
|
@@ -17285,7 +17063,7 @@ async function privateFile(path, limit) {
|
|
|
17285
17063
|
const encodedPath = Buffer.from(path).toString("base64");
|
|
17286
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`;
|
|
17287
17065
|
try {
|
|
17288
|
-
const { stdout } = await
|
|
17066
|
+
const { stdout } = await run(
|
|
17289
17067
|
"powershell.exe",
|
|
17290
17068
|
[
|
|
17291
17069
|
"-NoProfile",
|
|
@@ -17354,7 +17132,7 @@ function directory(state) {
|
|
|
17354
17132
|
const uri = state.workingDirectories?.[0];
|
|
17355
17133
|
if (typeof uri !== "string" || !uri.startsWith("file:")) return;
|
|
17356
17134
|
try {
|
|
17357
|
-
return
|
|
17135
|
+
return fileURLToPath(uri);
|
|
17358
17136
|
} catch {
|
|
17359
17137
|
return;
|
|
17360
17138
|
}
|
|
@@ -17375,7 +17153,7 @@ function confirmation(toolCall) {
|
|
|
17375
17153
|
const paths = [before, after].filter((uri) => typeof uri === "string");
|
|
17376
17154
|
const names = [...new Set(paths)].map((uri) => {
|
|
17377
17155
|
try {
|
|
17378
|
-
return
|
|
17156
|
+
return fileURLToPath(uri);
|
|
17379
17157
|
} catch {
|
|
17380
17158
|
return uri;
|
|
17381
17159
|
}
|
|
@@ -17402,8 +17180,7 @@ function confirmation(toolCall) {
|
|
|
17402
17180
|
})) : [];
|
|
17403
17181
|
if (unavailable) choices = choices.filter((choice) => choice.kind === "deny");
|
|
17404
17182
|
if (oversized) detail = Buffer.from(detail).subarray(0, MAX_PREVIEW).toString("utf8");
|
|
17405
|
-
if (unavailable || !choices.length)
|
|
17406
|
-
detail = [detail, SCREEN_PREVIEW].filter(Boolean).join("\n\n");
|
|
17183
|
+
if (unavailable || !choices.length) detail = [detail, NO_PREVIEW].filter(Boolean).join("\n\n");
|
|
17407
17184
|
return { detail, choices };
|
|
17408
17185
|
}
|
|
17409
17186
|
function messages3(chat) {
|
|
@@ -17747,11 +17524,11 @@ var VSCode = class {
|
|
|
17747
17524
|
|
|
17748
17525
|
// src/harness/opencode.ts
|
|
17749
17526
|
import { join as join7 } from "node:path";
|
|
17750
|
-
import { promisify as
|
|
17527
|
+
import { promisify as promisify4 } from "node:util";
|
|
17751
17528
|
import { createHash as createHash4 } from "node:crypto";
|
|
17752
|
-
import { execFile as
|
|
17529
|
+
import { execFile as execFile4, spawn as spawn3 } from "node:child_process";
|
|
17753
17530
|
import { stat as stat3, readlink, readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
|
|
17754
|
-
var
|
|
17531
|
+
var run2 = promisify4(execFile4);
|
|
17755
17532
|
var REQUEST_MS4 = 4e3;
|
|
17756
17533
|
var MAX_BYTES3 = 8 * 1024 * 1024;
|
|
17757
17534
|
var MAX_SESSIONS4 = 100;
|
|
@@ -17831,7 +17608,7 @@ async function endpoints() {
|
|
|
17831
17608
|
if (process.env.DASHUP_LIVE_DISCOVERY === "0") return [];
|
|
17832
17609
|
if (process.platform === "linux") return linuxEndpoints();
|
|
17833
17610
|
if (process.platform === "darwin") {
|
|
17834
|
-
const { stdout } = await
|
|
17611
|
+
const { stdout } = await run2(
|
|
17835
17612
|
"lsof",
|
|
17836
17613
|
["-nP", "-a", "-c", "opencode", "-iTCP", "-sTCP:LISTEN", "-Fn"],
|
|
17837
17614
|
{ timeout: REQUEST_MS4, maxBuffer: MAX_BYTES3 }
|
|
@@ -17848,7 +17625,7 @@ async function endpoints() {
|
|
|
17848
17625
|
}
|
|
17849
17626
|
if (process.platform === "win32") {
|
|
17850
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";
|
|
17851
|
-
const { stdout } = await
|
|
17628
|
+
const { stdout } = await run2(
|
|
17852
17629
|
"powershell.exe",
|
|
17853
17630
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
17854
17631
|
{ timeout: REQUEST_MS4, maxBuffer: MAX_BYTES3 }
|
|
@@ -17929,7 +17706,7 @@ ${always.join("\n")}` : ""
|
|
|
17929
17706
|
}
|
|
17930
17707
|
if (unavailable) detail += `
|
|
17931
17708
|
|
|
17932
|
-
${
|
|
17709
|
+
${NO_PREVIEW}`;
|
|
17933
17710
|
return {
|
|
17934
17711
|
id: permission.id,
|
|
17935
17712
|
title: `Allow ${String(permission.permission ?? "tool")}?`,
|
|
@@ -18130,7 +17907,7 @@ var Server = class {
|
|
|
18130
17907
|
if (!permissionApproval(permission, history2, cwd).choices.some(
|
|
18131
17908
|
(value2) => value2.id === choice
|
|
18132
17909
|
))
|
|
18133
|
-
throw new Error(
|
|
17910
|
+
throw new Error(NO_PREVIEW);
|
|
18134
17911
|
}
|
|
18135
17912
|
await this.request(`/permission/${encodeURIComponent(approvalId)}/reply`, cwd, {
|
|
18136
17913
|
reply: choice
|
|
@@ -18217,7 +17994,7 @@ var Server = class {
|
|
|
18217
17994
|
}
|
|
18218
17995
|
};
|
|
18219
17996
|
function serve(cwd) {
|
|
18220
|
-
const child =
|
|
17997
|
+
const child = spawn3("opencode", ["serve", "--hostname", "127.0.0.1", "--port", "0"], {
|
|
18221
17998
|
cwd,
|
|
18222
17999
|
stdio: ["ignore", "pipe", "ignore"],
|
|
18223
18000
|
shell: process.platform === "win32"
|
|
@@ -18342,7 +18119,7 @@ var LiveHarnesses = class {
|
|
|
18342
18119
|
async agents() {
|
|
18343
18120
|
const harnesses = ["claude", "codex", "opencode"];
|
|
18344
18121
|
const runs = await Promise.all(
|
|
18345
|
-
harnesses.map((harness) =>
|
|
18122
|
+
harnesses.map((harness) => promisify5(execFile5)(harness, ["--version"], {
|
|
18346
18123
|
timeout: 1e4,
|
|
18347
18124
|
shell: process.platform === "win32"
|
|
18348
18125
|
}).then(
|
|
@@ -18395,10 +18172,10 @@ var LiveHarnesses = class {
|
|
|
18395
18172
|
|
|
18396
18173
|
// src/harness/queue.ts
|
|
18397
18174
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18398
|
-
import { spawn as
|
|
18175
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
18399
18176
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
18400
18177
|
import { join as join8, dirname as dirname5, basename as basename3 } from "node:path";
|
|
18401
|
-
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";
|
|
18402
18179
|
var CODEX_VERSION = "0.160.1";
|
|
18403
18180
|
var REQUEST_MS5 = 1e4;
|
|
18404
18181
|
var MAX_BYTES4 = 1024 * 1024;
|
|
@@ -18419,7 +18196,7 @@ function packageVersion(executable) {
|
|
|
18419
18196
|
let directory2 = dirname5(executable);
|
|
18420
18197
|
for (let depth = 0; depth < 8; depth++, directory2 = dirname5(directory2)) {
|
|
18421
18198
|
const path = join8(directory2, "package.json");
|
|
18422
|
-
if (!
|
|
18199
|
+
if (!existsSync4(path)) continue;
|
|
18423
18200
|
const value2 = JSON.parse(readFileSync4(path, "utf8"));
|
|
18424
18201
|
if (value2.name?.startsWith("@openai/codex-")) return value2.version;
|
|
18425
18202
|
}
|
|
@@ -18520,7 +18297,7 @@ function validateEvidence(owner) {
|
|
|
18520
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))
|
|
18521
18298
|
throw new Error("The original native history storage changed");
|
|
18522
18299
|
const path = join8(owner.home, "thread_history_1.sqlite");
|
|
18523
|
-
if (
|
|
18300
|
+
if (existsSync4(path) && identity(path).path !== path)
|
|
18524
18301
|
throw new Error("Native history is outside the original home");
|
|
18525
18302
|
}
|
|
18526
18303
|
function nativeReceipt(owner, clientId) {
|
|
@@ -18537,7 +18314,7 @@ function nativeReceipt(owner, clientId) {
|
|
|
18537
18314
|
queue.close();
|
|
18538
18315
|
}
|
|
18539
18316
|
const path = join8(owner.home, "thread_history_1.sqlite");
|
|
18540
|
-
if (!
|
|
18317
|
+
if (!existsSync4(path)) return { queueId };
|
|
18541
18318
|
const history = new DatabaseSync3(path, { readOnly: true });
|
|
18542
18319
|
try {
|
|
18543
18320
|
const rows = history.prepare(
|
|
@@ -18592,7 +18369,7 @@ var Writer = class {
|
|
|
18592
18369
|
tail = "";
|
|
18593
18370
|
pending = /* @__PURE__ */ new Map();
|
|
18594
18371
|
constructor(owner) {
|
|
18595
|
-
this.process =
|
|
18372
|
+
this.process = spawn4(owner.executable, ["app-server", "--listen", "stdio://"], {
|
|
18596
18373
|
cwd: owner.cwd,
|
|
18597
18374
|
env: { ...process.env, CODEX_HOME: owner.home },
|
|
18598
18375
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -18751,6 +18528,8 @@ var NativeQueue = class {
|
|
|
18751
18528
|
// src/connector.ts
|
|
18752
18529
|
var EPHEMERAL_TTL_MS = 6e4;
|
|
18753
18530
|
var HOME_RECENT = 20;
|
|
18531
|
+
var FOLLOW_MS = 5 * 6e4;
|
|
18532
|
+
var RESCAN_MS = 1e3;
|
|
18754
18533
|
var Command = z2.object({
|
|
18755
18534
|
type: z2.literal("command"),
|
|
18756
18535
|
requestId: z2.string().min(1),
|
|
@@ -18763,8 +18542,6 @@ var Command = z2.object({
|
|
|
18763
18542
|
z2.object({ kind: z2.literal("thread.queue"), text: z2.string().min(1).max(1e5) }).strict(),
|
|
18764
18543
|
z2.object({ kind: z2.literal("thread.stop") }).strict(),
|
|
18765
18544
|
z2.object({ kind: z2.literal("approval.respond"), approvalId: z2.string(), choice: z2.string() }).strict(),
|
|
18766
|
-
z2.object({ kind: z2.literal("screen.start"), sessionId: z2.string().min(1).max(128) }).strict(),
|
|
18767
|
-
z2.object({ kind: z2.literal("screen.stop"), sessionId: z2.string().min(1).max(128) }).strict(),
|
|
18768
18545
|
z2.object({
|
|
18769
18546
|
kind: z2.literal("session.start"),
|
|
18770
18547
|
harness: z2.enum(["claude", "codex", "opencode"]),
|
|
@@ -18790,31 +18567,13 @@ var Connector = class {
|
|
|
18790
18567
|
autoConnect: false
|
|
18791
18568
|
});
|
|
18792
18569
|
provideClient(() => this.client);
|
|
18793
|
-
this.
|
|
18794
|
-
config2,
|
|
18795
|
-
(pair) => this.isActive(pair.id),
|
|
18796
|
-
(pair, content) => this.send(pair, content)
|
|
18797
|
-
);
|
|
18798
|
-
this.discovery = new Discovery(
|
|
18799
|
-
config2.device.id,
|
|
18800
|
-
{
|
|
18801
|
-
read: true,
|
|
18802
|
-
send: false,
|
|
18803
|
-
queue: false,
|
|
18804
|
-
stop: false,
|
|
18805
|
-
approve: false,
|
|
18806
|
-
screen: config2.screen && Viewer.available(),
|
|
18807
|
-
control: config2.control && controlAvailable()
|
|
18808
|
-
},
|
|
18809
|
-
config2.localSecret
|
|
18810
|
-
);
|
|
18570
|
+
this.discovery = new Discovery(config2.device.id, config2.localSecret);
|
|
18811
18571
|
this.pairings = createLiveQuery(() => this.client.bind(Pairing).where({ device: config2.device.id }).whereNull("revokedAt").clearLimit());
|
|
18812
18572
|
this.relays = createLiveQuery(() => this.client.bind(Relay).where({ recipient: config2.device.id }).orderBy("createdAt", "desc").orderBy("id", "desc").clearLimit());
|
|
18813
18573
|
}
|
|
18814
18574
|
config;
|
|
18815
18575
|
client;
|
|
18816
18576
|
discovery;
|
|
18817
|
-
viewer;
|
|
18818
18577
|
pairings;
|
|
18819
18578
|
relays;
|
|
18820
18579
|
nativeQueue;
|
|
@@ -18825,8 +18584,17 @@ var Connector = class {
|
|
|
18825
18584
|
retry;
|
|
18826
18585
|
stopWatching = () => {
|
|
18827
18586
|
};
|
|
18828
|
-
|
|
18829
|
-
|
|
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
|
+
};
|
|
18830
18598
|
releases = [];
|
|
18831
18599
|
announced = /* @__PURE__ */ new Set();
|
|
18832
18600
|
listed = /* @__PURE__ */ new Map();
|
|
@@ -18838,6 +18606,8 @@ var Connector = class {
|
|
|
18838
18606
|
snapshots = /* @__PURE__ */ new Set();
|
|
18839
18607
|
nativeSnapshots = /* @__PURE__ */ new Map();
|
|
18840
18608
|
queueReceipts = /* @__PURE__ */ new Map();
|
|
18609
|
+
// The conversations phones are reading, by pairing and thread.
|
|
18610
|
+
follows = /* @__PURE__ */ new Map();
|
|
18841
18611
|
// The content key behind each pairing code on show, until a phone pairs with it.
|
|
18842
18612
|
invitations = /* @__PURE__ */ new Map();
|
|
18843
18613
|
async start() {
|
|
@@ -18854,8 +18624,6 @@ var Connector = class {
|
|
|
18854
18624
|
this.releases.push(this.harnesses.watch(this.rescan));
|
|
18855
18625
|
this.releases.push(
|
|
18856
18626
|
this.pairings.retain(() => {
|
|
18857
|
-
if (this.pairings.snapshot().status !== "ready") this.viewer.close();
|
|
18858
|
-
else this.viewer.revoke(new Set(this.activePairs().map((pair) => pair.id)));
|
|
18859
18627
|
this.work = this.work.then(async () => {
|
|
18860
18628
|
if (this.pairings.snapshot().status !== "ready") return;
|
|
18861
18629
|
for (const pair of this.activePairs()) {
|
|
@@ -18935,7 +18703,6 @@ var Connector = class {
|
|
|
18935
18703
|
this.client.send("dashup:presence", { device: this.config.device.id });
|
|
18936
18704
|
});
|
|
18937
18705
|
this.client.on("error", () => this.report(new Error("Connection interrupted. Dashup will reconnect.")));
|
|
18938
|
-
this.client.on("disconnected", () => this.viewer.close());
|
|
18939
18706
|
await this.client.reconnect();
|
|
18940
18707
|
process.stdout.write(
|
|
18941
18708
|
`Dashup is connected. ${this.discovery.projects.size} existing projects found.
|
|
@@ -18949,29 +18716,16 @@ var Connector = class {
|
|
|
18949
18716
|
return invite;
|
|
18950
18717
|
}
|
|
18951
18718
|
stop() {
|
|
18719
|
+
this.stopped = true;
|
|
18952
18720
|
this.stopWatching();
|
|
18953
18721
|
for (const release of this.releases.splice(0)) release();
|
|
18954
18722
|
clearTimeout(this.retry);
|
|
18723
|
+
clearTimeout(this.rescanning);
|
|
18955
18724
|
this.client.dispose();
|
|
18956
|
-
this.viewer.close();
|
|
18957
18725
|
this.nativeQueue.stop();
|
|
18958
18726
|
this.harnesses.stop();
|
|
18959
18727
|
for (const agent of this.agents.values()) agent.socket.destroy();
|
|
18960
18728
|
}
|
|
18961
|
-
async permissions(screen, control) {
|
|
18962
|
-
this.config.screen = screen;
|
|
18963
|
-
this.config.control = screen && control;
|
|
18964
|
-
this.discovery.capabilities = {
|
|
18965
|
-
...this.discovery.capabilities,
|
|
18966
|
-
screen: screen && Viewer.available(),
|
|
18967
|
-
control: this.config.control && controlAvailable()
|
|
18968
|
-
};
|
|
18969
|
-
const sessions = screen ? [] : [...this.viewer.sessions];
|
|
18970
|
-
if (!screen) this.viewer.close();
|
|
18971
|
-
saveConfig(this.config);
|
|
18972
|
-
for (const [id, current] of sessions) await this.viewer.sendClose(id, current);
|
|
18973
|
-
await this.queueChange();
|
|
18974
|
-
}
|
|
18975
18729
|
activePairs() {
|
|
18976
18730
|
return this.pairings.snapshot().items;
|
|
18977
18731
|
}
|
|
@@ -19000,7 +18754,7 @@ var Connector = class {
|
|
|
19000
18754
|
);
|
|
19001
18755
|
if (envelope.ciphertext.length > MAX_ENVELOPE_BYTES * 2)
|
|
19002
18756
|
throw new Error(
|
|
19003
|
-
"This update exceeds the encrypted message limit.
|
|
18757
|
+
"This update exceeds the encrypted message limit. View the full conversation on your computer."
|
|
19004
18758
|
);
|
|
19005
18759
|
this.config.outbox.push(envelope);
|
|
19006
18760
|
saveConfig(this.config);
|
|
@@ -19013,7 +18767,7 @@ var Connector = class {
|
|
|
19013
18767
|
saveConfig(this.config);
|
|
19014
18768
|
}
|
|
19015
18769
|
flush() {
|
|
19016
|
-
const
|
|
18770
|
+
const run4 = this.drain.catch(() => {
|
|
19017
18771
|
}).then(async () => {
|
|
19018
18772
|
this.discardLegacySnapshots();
|
|
19019
18773
|
while (this.config.outbox.length && this.client.isConnected) {
|
|
@@ -19023,8 +18777,8 @@ var Connector = class {
|
|
|
19023
18777
|
saveConfig(this.config);
|
|
19024
18778
|
}
|
|
19025
18779
|
});
|
|
19026
|
-
this.drain =
|
|
19027
|
-
return
|
|
18780
|
+
this.drain = run4;
|
|
18781
|
+
return run4.catch((error) => {
|
|
19028
18782
|
this.report(error);
|
|
19029
18783
|
clearTimeout(this.retry);
|
|
19030
18784
|
this.retry = setTimeout(() => {
|
|
@@ -19065,7 +18819,6 @@ var Connector = class {
|
|
|
19065
18819
|
return {
|
|
19066
18820
|
type: "projects",
|
|
19067
18821
|
version,
|
|
19068
|
-
screen: this.discovery.capabilities.screen,
|
|
19069
18822
|
agents: this.startable,
|
|
19070
18823
|
folders: this.discovery.folders(),
|
|
19071
18824
|
projects: [...this.listedProjects.values()],
|
|
@@ -19099,15 +18852,16 @@ var Connector = class {
|
|
|
19099
18852
|
// transcript costs one rescan rather than one each.
|
|
19100
18853
|
queueChange() {
|
|
19101
18854
|
if (this.pendingChange) return this.pendingChange;
|
|
19102
|
-
const
|
|
18855
|
+
const run4 = this.work.then(() => {
|
|
19103
18856
|
this.pendingChange = void 0;
|
|
19104
18857
|
return this.changed();
|
|
19105
18858
|
});
|
|
19106
|
-
this.pendingChange =
|
|
19107
|
-
this.work =
|
|
19108
|
-
return
|
|
18859
|
+
this.pendingChange = run4;
|
|
18860
|
+
this.work = run4.catch((error) => this.report(error));
|
|
18861
|
+
return run4;
|
|
19109
18862
|
}
|
|
19110
18863
|
async changed() {
|
|
18864
|
+
if (this.stopped) return;
|
|
19111
18865
|
const previous = new Map(
|
|
19112
18866
|
[...this.discovery.projects.values()].map((project) => [project.id, project])
|
|
19113
18867
|
);
|
|
@@ -19142,9 +18896,19 @@ var Connector = class {
|
|
|
19142
18896
|
const projects = [...this.discovery.projects.values()];
|
|
19143
18897
|
this.stopWatching();
|
|
19144
18898
|
this.stopWatching = this.discovery.watch(this.rescan);
|
|
18899
|
+
await this.stream();
|
|
19145
18900
|
for (const project of projects) {
|
|
19146
|
-
|
|
19147
|
-
|
|
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);
|
|
19148
18912
|
}
|
|
19149
18913
|
for (const { project, thread, session } of live) {
|
|
19150
18914
|
const snapshot = this.threadState(project, thread);
|
|
@@ -19167,6 +18931,40 @@ var Connector = class {
|
|
|
19167
18931
|
for (const pair of this.activePairs()) await this.listProjects(pair);
|
|
19168
18932
|
await this.publishQueueReceipts();
|
|
19169
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
|
+
}
|
|
19170
18968
|
// A saved session nothing else has open continues headless when its agent runs here. One a
|
|
19171
18969
|
// native process owns takes queued messages instead.
|
|
19172
18970
|
refreshResumes() {
|
|
@@ -19218,38 +19016,23 @@ var Connector = class {
|
|
|
19218
19016
|
}
|
|
19219
19017
|
async receive(envelope) {
|
|
19220
19018
|
const pair = this.activePairs().find((item) => item.id === envelope.pairing);
|
|
19221
|
-
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")
|
|
19222
19020
|
return;
|
|
19223
19021
|
const key = this.config.pairs[pair.id];
|
|
19224
19022
|
if (envelope.sequence <= key.received) return;
|
|
19225
19023
|
const content = open(key.key, envelope);
|
|
19226
|
-
if (content.type !== "command"
|
|
19024
|
+
if (content.type !== "command" || content.projectId !== envelope.project || content.requestId !== envelope.id)
|
|
19227
19025
|
return;
|
|
19228
|
-
|
|
19229
|
-
if (content.type === "command") Command.parse(content);
|
|
19026
|
+
Command.parse(content);
|
|
19230
19027
|
key.received = envelope.sequence;
|
|
19231
|
-
|
|
19232
|
-
this.config.unconfirmed[content.requestId] = {
|
|
19233
|
-
pairing: pair.id,
|
|
19234
|
-
project: content.projectId
|
|
19235
|
-
};
|
|
19028
|
+
this.config.unconfirmed[content.requestId] = { pairing: pair.id, project: content.projectId };
|
|
19236
19029
|
saveConfig(this.config);
|
|
19237
|
-
if (content.type === "signal") {
|
|
19238
|
-
this.viewer.signal(pair, content.projectId, content.sessionId, content.signal);
|
|
19239
|
-
return;
|
|
19240
|
-
}
|
|
19241
|
-
if (content.action.kind === "screen.start") {
|
|
19242
|
-
void this.execute(pair, content, envelope).catch((error) => this.report(error));
|
|
19243
|
-
return;
|
|
19244
|
-
}
|
|
19245
19030
|
await this.execute(pair, content, envelope);
|
|
19246
19031
|
}
|
|
19247
19032
|
async execute(pair, content, envelope) {
|
|
19248
19033
|
let queue = void 0;
|
|
19249
19034
|
try {
|
|
19250
|
-
if (["
|
|
19251
|
-
content.action.kind
|
|
19252
|
-
) && 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)
|
|
19253
19036
|
throw new Error("Command expired. Try again while this computer is online.");
|
|
19254
19037
|
const outcome = await this.command(pair, content);
|
|
19255
19038
|
delete this.config.unconfirmed[content.requestId];
|
|
@@ -19402,12 +19185,20 @@ var Connector = class {
|
|
|
19402
19185
|
if (!thread) throw new Error("Thread not found");
|
|
19403
19186
|
const messages5 = this.discovery.read(thread, this.attached(thread)?.messages);
|
|
19404
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
|
+
});
|
|
19405
19196
|
return this.thread(pair, project, thread, history);
|
|
19406
19197
|
}
|
|
19407
19198
|
case "thread.send": {
|
|
19408
19199
|
if (!thread || !thread.capabilities.send)
|
|
19409
19200
|
throw new Error(
|
|
19410
|
-
"This session has no live reply connection.
|
|
19201
|
+
"This session has no live reply connection. Use the existing agent on your computer."
|
|
19411
19202
|
);
|
|
19412
19203
|
const location2 = this.discovery.locations.get(thread.id);
|
|
19413
19204
|
if (location2?.live?.send) return location2.live.send(command2.requestId, command2.action.text);
|
|
@@ -19428,7 +19219,7 @@ var Connector = class {
|
|
|
19428
19219
|
const agent = thread && this.attached(thread);
|
|
19429
19220
|
if (!agent || agent.socket.destroyed)
|
|
19430
19221
|
throw new Error(
|
|
19431
|
-
"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."
|
|
19432
19223
|
);
|
|
19433
19224
|
return commandAcp(agent, command2.requestId, { kind: "cancel" });
|
|
19434
19225
|
}
|
|
@@ -19462,7 +19253,8 @@ var Connector = class {
|
|
|
19462
19253
|
return native.approve(command2.requestId, approval4.id, action.choice);
|
|
19463
19254
|
}
|
|
19464
19255
|
const agent = this.attached(thread);
|
|
19465
|
-
if (!agent)
|
|
19256
|
+
if (!agent)
|
|
19257
|
+
throw new Error("Approval requires an attached ACP session. Answer it on your computer.");
|
|
19466
19258
|
const [request2, approval3] = [...agent.approvals].find(([, item]) => item.id === action.approvalId) ?? [];
|
|
19467
19259
|
if (!approval3?.choices.some((choice) => choice.id === action.choice))
|
|
19468
19260
|
throw new Error("Approval is no longer available");
|
|
@@ -19472,12 +19264,6 @@ var Connector = class {
|
|
|
19472
19264
|
choice: action.choice
|
|
19473
19265
|
});
|
|
19474
19266
|
}
|
|
19475
|
-
case "screen.start":
|
|
19476
|
-
if (!this.config.screen) throw new Error("Enable screen access on this computer first");
|
|
19477
|
-
return this.viewer.start(pair, project.id, command2.action.sessionId);
|
|
19478
|
-
case "screen.stop":
|
|
19479
|
-
this.viewer.stop(pair, command2.action.sessionId);
|
|
19480
|
-
return;
|
|
19481
19267
|
}
|
|
19482
19268
|
}
|
|
19483
19269
|
// Starts an agent in a project, or in a new folder inside one this computer offers. Commands run
|
|
@@ -19518,24 +19304,24 @@ var Connector = class {
|
|
|
19518
19304
|
};
|
|
19519
19305
|
|
|
19520
19306
|
// src/install.ts
|
|
19521
|
-
import { existsSync as
|
|
19307
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
19522
19308
|
import { homedir as homedir6, userInfo } from "node:os";
|
|
19523
|
-
import { promisify as
|
|
19524
|
-
import { fileURLToPath as
|
|
19525
|
-
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";
|
|
19526
19312
|
import { cp, mkdir as mkdir2, rm, writeFile } from "node:fs/promises";
|
|
19527
19313
|
import { dirname as dirname6, join as join10, relative, sep } from "node:path";
|
|
19528
|
-
var
|
|
19314
|
+
var run3 = promisify6(execFile6);
|
|
19529
19315
|
var xml = (value2) => value2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
19530
19316
|
var NODE_FLAGS = ["--disable-warning=ExperimentalWarning"];
|
|
19531
|
-
var systemctl = (...args2) =>
|
|
19317
|
+
var systemctl = (...args2) => run3("systemctl", ["--user", ...args2], {
|
|
19532
19318
|
env: { ...process.env, XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR ?? `/run/user/${userInfo().uid}` }
|
|
19533
19319
|
});
|
|
19534
19320
|
var serviceFile = () => process.platform === "darwin" ? join10(homedir6(), "Library/LaunchAgents/com.dashup.connector.plist") : process.platform === "win32" ? join10(
|
|
19535
19321
|
process.env.APPDATA ?? join10(homedir6(), "AppData/Roaming"),
|
|
19536
19322
|
"Microsoft/Windows/Start Menu/Programs/Startup/Dashup.vbs"
|
|
19537
19323
|
) : join10(homedir6(), ".config/systemd/user/dashup.service");
|
|
19538
|
-
var startupInstalled = () =>
|
|
19324
|
+
var startupInstalled = () => existsSync5(serviceFile());
|
|
19539
19325
|
function service(platform, node, entry, config2) {
|
|
19540
19326
|
const command2 = [node, ...NODE_FLAGS, entry];
|
|
19541
19327
|
if (platform === "darwin")
|
|
@@ -19563,7 +19349,7 @@ WantedBy=default.target
|
|
|
19563
19349
|
`;
|
|
19564
19350
|
}
|
|
19565
19351
|
async function durableEntry() {
|
|
19566
|
-
const entry =
|
|
19352
|
+
const entry = fileURLToPath2(import.meta.url);
|
|
19567
19353
|
if (!entry.endsWith(`${sep}dist${sep}index.js`))
|
|
19568
19354
|
throw new Error(
|
|
19569
19355
|
"Build the connector before installing startup: pnpm --filter @dashup/connector build"
|
|
@@ -19586,13 +19372,13 @@ async function installStartup() {
|
|
|
19586
19372
|
service(process.platform, process.execPath, await durableEntry(), configPath())
|
|
19587
19373
|
);
|
|
19588
19374
|
if (process.platform === "darwin") {
|
|
19589
|
-
await
|
|
19375
|
+
await run3("launchctl", ["unload", path]).catch(() => {
|
|
19590
19376
|
});
|
|
19591
|
-
await
|
|
19377
|
+
await run3("launchctl", ["load", path]);
|
|
19592
19378
|
} else if (process.platform === "win32") {
|
|
19593
|
-
|
|
19379
|
+
spawn5("wscript.exe", [path], { detached: true, stdio: "ignore" }).unref();
|
|
19594
19380
|
} else {
|
|
19595
|
-
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(
|
|
19596
19382
|
() => void 0,
|
|
19597
19383
|
() => `It stops when you log out. To keep it running, run sudo loginctl enable-linger ${userInfo().username}.`
|
|
19598
19384
|
);
|
|
@@ -19604,7 +19390,7 @@ async function installStartup() {
|
|
|
19604
19390
|
}
|
|
19605
19391
|
async function uninstallStartup() {
|
|
19606
19392
|
const path = serviceFile();
|
|
19607
|
-
if (process.platform === "darwin") await
|
|
19393
|
+
if (process.platform === "darwin") await run3("launchctl", ["unload", path]).catch(() => {
|
|
19608
19394
|
});
|
|
19609
19395
|
else if (process.platform !== "win32")
|
|
19610
19396
|
await systemctl("disable", "dashup.service").catch(() => {
|
|
@@ -19709,22 +19495,6 @@ async function main() {
|
|
|
19709
19495
|
if (!response.ok) throw new Error("Could not stop Dashup");
|
|
19710
19496
|
return;
|
|
19711
19497
|
}
|
|
19712
|
-
if (command === "permissions") {
|
|
19713
|
-
config.screen = args.includes("--screen");
|
|
19714
|
-
config.control = config.screen && args.includes("--control");
|
|
19715
|
-
const remote = await running("/permissions", {
|
|
19716
|
-
method: "POST",
|
|
19717
|
-
body: JSON.stringify({ screen: config.screen, control: config.control }),
|
|
19718
|
-
signal: AbortSignal.timeout(5e3)
|
|
19719
|
-
}).catch(refused);
|
|
19720
|
-
if (remote && !remote.ok) throw new Error("Could not change connector permissions");
|
|
19721
|
-
if (!remote) saveConfig({ ...loadConfig(), screen: config.screen, control: config.control });
|
|
19722
|
-
process.stdout.write(
|
|
19723
|
-
`Screen ${config.screen ? "enabled" : "disabled"}, control ${config.control ? "enabled" : "disabled"}.
|
|
19724
|
-
`
|
|
19725
|
-
);
|
|
19726
|
-
return;
|
|
19727
|
-
}
|
|
19728
19498
|
if (command === "acp") {
|
|
19729
19499
|
const native = args[1] === "--" ? args.slice(2) : args.slice(1);
|
|
19730
19500
|
const [executable, ...agentArgs] = native;
|
|
@@ -19733,18 +19503,7 @@ async function main() {
|
|
|
19733
19503
|
return proxyAcp(config, executable, agentArgs);
|
|
19734
19504
|
}
|
|
19735
19505
|
if (command === "doctor" || command === "projects") {
|
|
19736
|
-
const
|
|
19737
|
-
const discovery = new Discovery(
|
|
19738
|
-
config.device?.id ?? "local",
|
|
19739
|
-
{
|
|
19740
|
-
read: true,
|
|
19741
|
-
send: false,
|
|
19742
|
-
approve: false,
|
|
19743
|
-
screen: config.screen && support.screen,
|
|
19744
|
-
control: config.control && support.control
|
|
19745
|
-
},
|
|
19746
|
-
config.localSecret
|
|
19747
|
-
);
|
|
19506
|
+
const discovery = new Discovery(config.device?.id ?? "local", config.localSecret);
|
|
19748
19507
|
discovery.scan();
|
|
19749
19508
|
const live = new LiveHarnesses();
|
|
19750
19509
|
try {
|
|
@@ -19759,7 +19518,6 @@ async function main() {
|
|
|
19759
19518
|
node: process.version,
|
|
19760
19519
|
paired: Boolean(config.secret),
|
|
19761
19520
|
platform: process.platform,
|
|
19762
|
-
screen: support,
|
|
19763
19521
|
projectCount: projects.length,
|
|
19764
19522
|
harnesses: Object.fromEntries(
|
|
19765
19523
|
Object.keys(HARNESSES).filter((harness) => harness !== "other").map((harness) => [
|
|
@@ -19793,7 +19551,7 @@ async function main() {
|
|
|
19793
19551
|
}
|
|
19794
19552
|
if (!["pair", "watch"].includes(command)) {
|
|
19795
19553
|
process.stdout.write(
|
|
19796
|
-
"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"
|
|
19797
19555
|
);
|
|
19798
19556
|
return;
|
|
19799
19557
|
}
|
|
@@ -19866,7 +19624,7 @@ async function main() {
|
|
|
19866
19624
|
response.end(JSON.stringify(connector.invite()));
|
|
19867
19625
|
return;
|
|
19868
19626
|
}
|
|
19869
|
-
if (
|
|
19627
|
+
if (request2.url !== "/attention" || request2.method !== "POST") {
|
|
19870
19628
|
response.writeHead(404).end();
|
|
19871
19629
|
return;
|
|
19872
19630
|
}
|
|
@@ -19875,12 +19633,6 @@ async function main() {
|
|
|
19875
19633
|
value2 += String(chunk);
|
|
19876
19634
|
if (value2.length > 2e4) throw new Error("Alert too large");
|
|
19877
19635
|
}
|
|
19878
|
-
if (request2.url === "/permissions") {
|
|
19879
|
-
const input2 = z3.object({ screen: z3.boolean(), control: z3.boolean() }).strict().parse(JSON.parse(value2));
|
|
19880
|
-
await connector.permissions(input2.screen, input2.control);
|
|
19881
|
-
response.end("{}");
|
|
19882
|
-
return;
|
|
19883
|
-
}
|
|
19884
19636
|
const input = Attention.parse(JSON.parse(value2));
|
|
19885
19637
|
await connector.attention(
|
|
19886
19638
|
input.projectId,
|