@dashup/connector 0.1.8 → 0.1.10
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 +495 -603
- 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,12 +2726,17 @@ 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;
|
|
2732
2733
|
var HISTORY_BATCH_BYTES = HISTORY_PAGE_BYTES;
|
|
2733
2734
|
var HISTORY_RECORD_BYTES = 32 * 1024;
|
|
2734
2735
|
var HISTORY_RECORD_CIPHERTEXT_BYTES = HISTORY_RECORD_BYTES + 16;
|
|
2736
|
+
var OUTPUT_PREVIEW = { lines: 12, chars: 2e3 };
|
|
2737
|
+
var HISTORY_ATTACHMENTS = 16;
|
|
2738
|
+
var MAX_ATTACHMENT_BYTES = 8 * 1024 * 1024;
|
|
2739
|
+
var ATTACHMENT_NAME_CHARS = 100;
|
|
2735
2740
|
var MESSAGE_ROLES = ["user", "assistant", "system", "tool"];
|
|
2736
2741
|
var HARNESSES = {
|
|
2737
2742
|
claude: "Claude Code",
|
|
@@ -2874,6 +2879,21 @@ function complexOverlapBytes(input, output) {
|
|
|
2874
2879
|
if (overlapBytes(input, output) && input.byteOffset < output.byteOffset)
|
|
2875
2880
|
throw new Error("complex overlap of input and output is not supported");
|
|
2876
2881
|
}
|
|
2882
|
+
function concatBytes(...arrays) {
|
|
2883
|
+
let sum = 0;
|
|
2884
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
2885
|
+
const a = arrays[i];
|
|
2886
|
+
abytes(a);
|
|
2887
|
+
sum += a.length;
|
|
2888
|
+
}
|
|
2889
|
+
const res = new Uint8Array(sum);
|
|
2890
|
+
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
|
2891
|
+
const a = arrays[i];
|
|
2892
|
+
res.set(a, pad);
|
|
2893
|
+
pad += a.length;
|
|
2894
|
+
}
|
|
2895
|
+
return res;
|
|
2896
|
+
}
|
|
2877
2897
|
function checkOpts(defaults, opts) {
|
|
2878
2898
|
aobject(defaults, "defaults");
|
|
2879
2899
|
aobject(opts, "opts");
|
|
@@ -3588,6 +3608,12 @@ function decryptContent(key, associatedData, sealed) {
|
|
|
3588
3608
|
).decrypt(ciphertextBytes(sealed.ciphertext));
|
|
3589
3609
|
return JSON.parse(bytesToUtf8(cleartext));
|
|
3590
3610
|
}
|
|
3611
|
+
var attachmentData = (pairing, id) => utf8ToBytes(JSON.stringify([PROTOCOL_VERSION, "attachment", pairing, id]));
|
|
3612
|
+
function sealAttachment(key, pairing, id, bytes, random) {
|
|
3613
|
+
const nonce = random(NONCE_BYTES);
|
|
3614
|
+
const cipher = xchacha20poly1305(hexToBytes(key), nonce, attachmentData(pairing, id));
|
|
3615
|
+
return concatBytes(nonce, cipher.encrypt(bytes));
|
|
3616
|
+
}
|
|
3591
3617
|
var PAIRING_CODE = new RegExp(
|
|
3592
3618
|
`^DASHUP:${PROTOCOL_VERSION}:([0-9A-F]{64}):([0-9A-F]{16}):([0-9A-F]{64}):(.+)$`
|
|
3593
3619
|
);
|
|
@@ -3615,8 +3641,6 @@ function loadConfig() {
|
|
|
3615
3641
|
api: PRODUCT.api,
|
|
3616
3642
|
secret: "",
|
|
3617
3643
|
pairs: {},
|
|
3618
|
-
screen: false,
|
|
3619
|
-
control: false,
|
|
3620
3644
|
outbox: [],
|
|
3621
3645
|
localSecret: ""
|
|
3622
3646
|
};
|
|
@@ -3642,10 +3666,14 @@ function saveConfig(config2) {
|
|
|
3642
3666
|
}
|
|
3643
3667
|
}
|
|
3644
3668
|
async function api(config2, path, body2, method = body2 ? "POST" : "GET") {
|
|
3669
|
+
const bytes = body2 instanceof Uint8Array;
|
|
3645
3670
|
const response = await fetch(`${config2.api}/v1${path}`, {
|
|
3646
3671
|
method,
|
|
3647
|
-
headers: {
|
|
3648
|
-
|
|
3672
|
+
headers: {
|
|
3673
|
+
"Content-Type": bytes ? "application/octet-stream" : "application/json",
|
|
3674
|
+
Authorization: `Bearer ${config2.secret}`
|
|
3675
|
+
},
|
|
3676
|
+
body: bytes ? new Uint8Array(body2) : body2 ? JSON.stringify(body2) : void 0
|
|
3649
3677
|
});
|
|
3650
3678
|
const value2 = await response.json();
|
|
3651
3679
|
if (!response.ok || !value2.success)
|
|
@@ -4017,7 +4045,7 @@ import * as ui from "@clack/prompts";
|
|
|
4017
4045
|
import { z } from "zod";
|
|
4018
4046
|
|
|
4019
4047
|
// package.json
|
|
4020
|
-
var version = "0.1.
|
|
4048
|
+
var version = "0.1.10";
|
|
4021
4049
|
|
|
4022
4050
|
// src/pair.ts
|
|
4023
4051
|
var Invitation = z.object({
|
|
@@ -4147,282 +4175,19 @@ async function link(config2, account) {
|
|
|
4147
4175
|
return true;
|
|
4148
4176
|
}
|
|
4149
4177
|
|
|
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
4178
|
// src/discovery.ts
|
|
4414
4179
|
import { homedir as homedir3 } from "node:os";
|
|
4415
4180
|
import { createHmac } from "node:crypto";
|
|
4416
4181
|
import { basename, dirname as dirname3, join as join3 } from "node:path";
|
|
4417
4182
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
4418
4183
|
import { zstdDecompressSync } from "node:zlib";
|
|
4419
|
-
import { watch, existsSync as
|
|
4184
|
+
import { watch, existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
4420
4185
|
|
|
4421
4186
|
// src/harness/history.ts
|
|
4422
4187
|
import { homedir as homedir2 } from "node:os";
|
|
4423
4188
|
import { DatabaseSync } from "node:sqlite";
|
|
4424
4189
|
import { join as join2, dirname as dirname2, resolve as resolve2, isAbsolute } from "node:path";
|
|
4425
|
-
import { closeSync as closeSync2, existsSync as
|
|
4190
|
+
import { closeSync as closeSync2, existsSync as existsSync2, openSync as openSync2, readSync, realpathSync, readdirSync, statSync } from "node:fs";
|
|
4426
4191
|
|
|
4427
4192
|
// src/harness/session.ts
|
|
4428
4193
|
var EARLIER = {
|
|
@@ -4437,8 +4202,15 @@ function agentHarness(name) {
|
|
|
4437
4202
|
if (/opencode/i.test(name)) return "opencode";
|
|
4438
4203
|
return void 0;
|
|
4439
4204
|
}
|
|
4205
|
+
var attached = (id, mime, data, name) => ({
|
|
4206
|
+
id,
|
|
4207
|
+
mime,
|
|
4208
|
+
bytes: Buffer.byteLength(data, "base64"),
|
|
4209
|
+
...name ? { name: name.slice(-ATTACHMENT_NAME_CHARS) } : {},
|
|
4210
|
+
read: () => Buffer.from(data, "base64")
|
|
4211
|
+
});
|
|
4440
4212
|
var MAX_PREVIEW = 64 * 1024;
|
|
4441
|
-
var
|
|
4213
|
+
var NO_PREVIEW = "Review the full request on your computer before approving.";
|
|
4442
4214
|
|
|
4443
4215
|
// src/harness/history.ts
|
|
4444
4216
|
var SAMPLE_BYTES = 128 * 1024;
|
|
@@ -4482,10 +4254,10 @@ function readRange(path, start, length) {
|
|
|
4482
4254
|
closeSync2(fd);
|
|
4483
4255
|
}
|
|
4484
4256
|
}
|
|
4485
|
-
function
|
|
4257
|
+
function described(path, until, describe2) {
|
|
4486
4258
|
const stamp = statSync(path);
|
|
4487
4259
|
const saved = metadata.get(path);
|
|
4488
|
-
if (saved && unchanged(saved.stamp, stamp)) return saved.
|
|
4260
|
+
if (saved && unchanged(saved.stamp, stamp)) return saved.value;
|
|
4489
4261
|
let head = readRange(path, 0, Math.min(stamp.size, SAMPLE_BYTES));
|
|
4490
4262
|
if (head.length < stamp.size && head.at(-1) !== 10) {
|
|
4491
4263
|
const chunks = [head];
|
|
@@ -4508,9 +4280,9 @@ function sample(path, until) {
|
|
|
4508
4280
|
);
|
|
4509
4281
|
if (start === head.length || tail.some(until)) break;
|
|
4510
4282
|
}
|
|
4511
|
-
const
|
|
4512
|
-
metadata.set(path, { stamp,
|
|
4513
|
-
return
|
|
4283
|
+
const value2 = describe2([...records(head), ...tail], stamp);
|
|
4284
|
+
metadata.set(path, { stamp, value: value2 });
|
|
4285
|
+
return value2;
|
|
4514
4286
|
}
|
|
4515
4287
|
var windowed = (path, messages5) => journals.get(path)?.start ? [EARLIER, ...messages5] : messages5;
|
|
4516
4288
|
function journal(path, latest = false) {
|
|
@@ -4552,7 +4324,7 @@ function journal(path, latest = false) {
|
|
|
4552
4324
|
return tail ? [...saved.rows, tail] : saved.rows;
|
|
4553
4325
|
}
|
|
4554
4326
|
function files(root, scan, depth = Infinity) {
|
|
4555
|
-
if (!
|
|
4327
|
+
if (!existsSync2(root)) {
|
|
4556
4328
|
watchParent(root, scan);
|
|
4557
4329
|
return [];
|
|
4558
4330
|
}
|
|
@@ -4569,8 +4341,8 @@ function files(root, scan, depth = Infinity) {
|
|
|
4569
4341
|
}
|
|
4570
4342
|
}
|
|
4571
4343
|
function watchParent(path, scan) {
|
|
4572
|
-
while (!
|
|
4573
|
-
if (
|
|
4344
|
+
while (!existsSync2(path) && dirname2(path) !== path) path = dirname2(path);
|
|
4345
|
+
if (existsSync2(path)) scan.paths.add(path);
|
|
4574
4346
|
}
|
|
4575
4347
|
function canonical(cwd) {
|
|
4576
4348
|
if (typeof cwd !== "string" || !isAbsolute(cwd)) return void 0;
|
|
@@ -4596,13 +4368,27 @@ function text(value2) {
|
|
|
4596
4368
|
const part = object(value2);
|
|
4597
4369
|
return part ? typeof part.text === "string" ? part.text : text(part.content) : "";
|
|
4598
4370
|
}
|
|
4371
|
+
var DATA_URL = /^data:([^;,]+);base64,/;
|
|
4372
|
+
function attachments(id, content) {
|
|
4373
|
+
return list(content).flatMap((part, index) => {
|
|
4374
|
+
const key = `${id}:${index}`;
|
|
4375
|
+
const name = [part?.title, part?.filename].find((value2) => typeof value2 === "string");
|
|
4376
|
+
const source = object(part?.source);
|
|
4377
|
+
if (source?.type === "base64" && typeof source.data === "string")
|
|
4378
|
+
return [attached(key, String(source.media_type), source.data, name)];
|
|
4379
|
+
const url2 = String(part?.image_url ?? part?.url ?? "");
|
|
4380
|
+
const data = DATA_URL.exec(url2);
|
|
4381
|
+
return data ? [attached(key, data[1], url2.slice(data[0].length), name)] : [];
|
|
4382
|
+
});
|
|
4383
|
+
}
|
|
4599
4384
|
function prose(id, role, content) {
|
|
4600
4385
|
const value2 = text(
|
|
4601
4386
|
Array.isArray(content) ? content.filter(
|
|
4602
4387
|
(part) => typeof part === "string" || ["text", "input_text", "output_text"].includes(part?.type)
|
|
4603
4388
|
) : content
|
|
4604
4389
|
);
|
|
4605
|
-
|
|
4390
|
+
const files2 = attachments(id, content);
|
|
4391
|
+
return value2 || files2.length ? [{ id, role, text: value2, ...files2.length ? { attachments: files2 } : {} }] : [];
|
|
4606
4392
|
}
|
|
4607
4393
|
var SUMMARY_LIMIT = 200;
|
|
4608
4394
|
var SUMMARY_FIELDS = [
|
|
@@ -4621,10 +4407,10 @@ var SUMMARY_FIELDS = [
|
|
|
4621
4407
|
var PATCH_FILE = /^\*\*\* (Add|Update|Delete) File: (.+)$/gm;
|
|
4622
4408
|
function describe(input) {
|
|
4623
4409
|
const fields = object(input);
|
|
4624
|
-
const
|
|
4410
|
+
const named2 = SUMMARY_FIELDS.map((key) => fields?.[key]).find(
|
|
4625
4411
|
(value3) => typeof value3 === "string" && value3.trim() !== ""
|
|
4626
4412
|
);
|
|
4627
|
-
const value2 = typeof input === "string" ? input :
|
|
4413
|
+
const value2 = typeof input === "string" ? input : named2 ?? (fields && Object.keys(fields).length ? JSON.stringify(fields) : "");
|
|
4628
4414
|
const paths = [...value2.matchAll(PATCH_FILE)].map((match) => match[2]).join(", ");
|
|
4629
4415
|
const first = value2.split("\n").find((line) => line.trim());
|
|
4630
4416
|
return (paths || first?.trim())?.slice(0, SUMMARY_LIMIT);
|
|
@@ -4642,9 +4428,10 @@ function added(path, content) {
|
|
|
4642
4428
|
...lines.map((line) => `+${line}`)
|
|
4643
4429
|
].join("\n");
|
|
4644
4430
|
}
|
|
4431
|
+
var binary = (diff) => diff.slice(0, 8e3).includes("\0");
|
|
4645
4432
|
function step(id, name, input, result) {
|
|
4646
4433
|
const summary = describe(input);
|
|
4647
|
-
const diff = result?.status === "ok" ? result.diff : void 0;
|
|
4434
|
+
const diff = result?.status === "ok" && result.diff && !binary(result.diff) ? result.diff : void 0;
|
|
4648
4435
|
return {
|
|
4649
4436
|
id,
|
|
4650
4437
|
role: "tool",
|
|
@@ -4655,7 +4442,8 @@ function step(id, name, input, result) {
|
|
|
4655
4442
|
...result?.status ? { status: result.status } : {},
|
|
4656
4443
|
...result?.status && result.ms !== void 0 ? { ms: result.ms } : {},
|
|
4657
4444
|
...diff ? { diff: true } : {}
|
|
4658
|
-
}
|
|
4445
|
+
},
|
|
4446
|
+
...result?.attachments?.length ? { attachments: result.attachments } : {}
|
|
4659
4447
|
};
|
|
4660
4448
|
}
|
|
4661
4449
|
var claudeHome = () => process.env.CLAUDE_CONFIG_DIR ?? join2(homedir2(), ".claude");
|
|
@@ -4711,16 +4499,18 @@ function claudeConversation(conversation, rows) {
|
|
|
4711
4499
|
...list(content).flatMap((part, position) => {
|
|
4712
4500
|
if (part?.type !== "tool_use") return [];
|
|
4713
4501
|
const result = results.get(part.id);
|
|
4502
|
+
const call = String(part.id ?? `${id}:${position}`);
|
|
4714
4503
|
return [
|
|
4715
4504
|
step(
|
|
4716
|
-
|
|
4505
|
+
call,
|
|
4717
4506
|
part.name,
|
|
4718
4507
|
part.input,
|
|
4719
4508
|
result && {
|
|
4720
4509
|
text: text(result.part.content),
|
|
4721
4510
|
status: result.part.is_error ? "error" : "ok",
|
|
4722
4511
|
ms: elapsed(row.timestamp, result.row.timestamp),
|
|
4723
|
-
diff: claudeChange(object(result.row.toolUseResult))
|
|
4512
|
+
diff: claudeChange(object(result.row.toolUseResult)),
|
|
4513
|
+
attachments: attachments(call, result.part.content)
|
|
4724
4514
|
}
|
|
4725
4515
|
)
|
|
4726
4516
|
];
|
|
@@ -4731,26 +4521,31 @@ function claudeConversation(conversation, rows) {
|
|
|
4731
4521
|
function claude(root, scan) {
|
|
4732
4522
|
for (const path of files(join2(root, "projects"), scan, 1).filter((file) => file.endsWith(".jsonl"))) {
|
|
4733
4523
|
try {
|
|
4734
|
-
const
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4524
|
+
const session = described(path, claudeMessage, (rows, stamp) => {
|
|
4525
|
+
const latest = [...rows].reverse();
|
|
4526
|
+
const owner = rows.find((row) => typeof row.sessionId === "string" && canonical(row.cwd));
|
|
4527
|
+
if (!owner) return void 0;
|
|
4528
|
+
const named2 = latest.find((row) => row.type === "custom-title" || row.type === "ai-title");
|
|
4529
|
+
const first = rows.find(
|
|
4530
|
+
(row) => row.type === "user" && !row.isMeta && !row.isSidechain && !injected(row.message?.content)
|
|
4531
|
+
);
|
|
4532
|
+
return {
|
|
4533
|
+
id: String(owner.sessionId),
|
|
4534
|
+
cwd: canonical(owner.cwd),
|
|
4535
|
+
title: title(
|
|
4536
|
+
named2?.customTitle ?? named2?.aiTitle ?? text(first?.message?.content),
|
|
4537
|
+
"Claude Code thread"
|
|
4538
|
+
),
|
|
4539
|
+
updatedAt: date(latest.find(claudeMessage)?.timestamp, stamp.mtimeMs)
|
|
4540
|
+
};
|
|
4541
|
+
});
|
|
4542
|
+
if (!session) {
|
|
4738
4543
|
scan.warnings.push(`Unrecognized Claude Code session: ${path}`);
|
|
4739
4544
|
continue;
|
|
4740
4545
|
}
|
|
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
4546
|
scan.sessions.push({
|
|
4746
4547
|
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),
|
|
4548
|
+
...session,
|
|
4754
4549
|
files: [path],
|
|
4755
4550
|
read: () => claudeMessages(path)
|
|
4756
4551
|
});
|
|
@@ -4794,15 +4589,17 @@ function codexMessages(path) {
|
|
|
4794
4589
|
const input = item.type === "function_call" ? parse(String(item.arguments)) ?? item.arguments : item.input;
|
|
4795
4590
|
const output = outputs.get(item.call_id);
|
|
4796
4591
|
const patch = item.name === "apply_patch" && typeof input === "string" ? input : "";
|
|
4592
|
+
const call = String(item.call_id ?? id);
|
|
4797
4593
|
return [
|
|
4798
4594
|
step(
|
|
4799
|
-
|
|
4595
|
+
call,
|
|
4800
4596
|
item.name,
|
|
4801
4597
|
input,
|
|
4802
4598
|
output && {
|
|
4803
4599
|
...codexResult(text(output.payload.output)),
|
|
4804
4600
|
ms: elapsed(row.timestamp, output.timestamp),
|
|
4805
|
-
diff: patch && applyPatch(patch)
|
|
4601
|
+
diff: patch && applyPatch(patch),
|
|
4602
|
+
attachments: attachments(call, output.payload.output)
|
|
4806
4603
|
}
|
|
4807
4604
|
)
|
|
4808
4605
|
];
|
|
@@ -4822,7 +4619,7 @@ function codexMessages(path) {
|
|
|
4822
4619
|
function codex(root, scan) {
|
|
4823
4620
|
const index = join2(root, "session_index.jsonl");
|
|
4824
4621
|
const names = /* @__PURE__ */ new Map();
|
|
4825
|
-
if (
|
|
4622
|
+
if (existsSync2(index)) {
|
|
4826
4623
|
scan.paths.add(root);
|
|
4827
4624
|
try {
|
|
4828
4625
|
for (const row of journal(index)) if (typeof row.id === "string") names.set(row.id, row);
|
|
@@ -4831,34 +4628,41 @@ function codex(root, scan) {
|
|
|
4831
4628
|
}
|
|
4832
4629
|
}
|
|
4833
4630
|
const titles = codexTitles(root);
|
|
4631
|
+
const indexed = existsSync2(index);
|
|
4834
4632
|
for (const path of [
|
|
4835
4633
|
...files(join2(root, "sessions"), scan),
|
|
4836
4634
|
...files(join2(root, "archived_sessions"), scan)
|
|
4837
4635
|
].filter((file) => file.endsWith(".jsonl"))) {
|
|
4838
4636
|
try {
|
|
4839
|
-
const
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4637
|
+
const session = described(path, codexItem, (rows, stamp) => {
|
|
4638
|
+
const owner = rows.find((row) => row.type === "session_meta")?.payload;
|
|
4639
|
+
const cwd = canonical(owner?.cwd);
|
|
4640
|
+
if (typeof owner?.id !== "string" || !cwd) return void 0;
|
|
4641
|
+
const first = rows.find(
|
|
4642
|
+
(row) => row.type === "response_item" && row.payload?.type === "message" && row.payload.role === "user" && !injected(row.payload.content)
|
|
4643
|
+
);
|
|
4644
|
+
const parent = owner.parent_thread_id;
|
|
4645
|
+
return {
|
|
4646
|
+
id: owner.id,
|
|
4647
|
+
cwd,
|
|
4648
|
+
asked: text(first?.payload?.content),
|
|
4649
|
+
parent: typeof parent === "string" ? parent : void 0,
|
|
4650
|
+
updatedAt: date([...rows].reverse().find(codexItem)?.timestamp, stamp.mtimeMs)
|
|
4651
|
+
};
|
|
4652
|
+
});
|
|
4653
|
+
if (!session) {
|
|
4844
4654
|
scan.warnings.push(`Unrecognized Codex session: ${path}`);
|
|
4845
4655
|
continue;
|
|
4846
4656
|
}
|
|
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
|
-
);
|
|
4657
|
+
const { asked, ...found } = session;
|
|
4851
4658
|
scan.sessions.push({
|
|
4852
4659
|
harness: "codex",
|
|
4853
|
-
|
|
4854
|
-
cwd,
|
|
4660
|
+
...found,
|
|
4855
4661
|
title: title(
|
|
4856
|
-
|
|
4662
|
+
names.get(found.id)?.thread_name ?? titles.get(found.id) ?? asked,
|
|
4857
4663
|
"Codex thread"
|
|
4858
4664
|
),
|
|
4859
|
-
|
|
4860
|
-
updatedAt: date(latest.find(codexItem)?.timestamp, statSync(path).mtimeMs),
|
|
4861
|
-
files: [path, ...existsSync3(index) ? [index] : []],
|
|
4665
|
+
files: [path, ...indexed ? [index] : []],
|
|
4862
4666
|
read: () => codexMessages(path)
|
|
4863
4667
|
});
|
|
4864
4668
|
} catch {
|
|
@@ -4877,6 +4681,10 @@ function openCodeParts(id, role, parts2) {
|
|
|
4877
4681
|
const key = String(part.id ?? `${id}:${index}`);
|
|
4878
4682
|
if (part.type === "text" && typeof part.text === "string")
|
|
4879
4683
|
return [{ id: key, role, text: part.text }];
|
|
4684
|
+
if (part.type === "file") {
|
|
4685
|
+
const files2 = attachments(key, [part]);
|
|
4686
|
+
return files2.length ? [{ id: key, role, text: "", attachments: files2 }] : [];
|
|
4687
|
+
}
|
|
4880
4688
|
if (part.type !== "tool") return [];
|
|
4881
4689
|
const state = object(part.state) ?? {};
|
|
4882
4690
|
const status2 = state.status === "completed" ? "ok" : state.status === "error" ? "error" : void 0;
|
|
@@ -4889,7 +4697,8 @@ function openCodeParts(id, role, parts2) {
|
|
|
4889
4697
|
text: text(state.output ?? state.error),
|
|
4890
4698
|
status: status2,
|
|
4891
4699
|
ms: elapsed(state.time?.start, state.time?.end),
|
|
4892
|
-
diff: openCodeChange(state)
|
|
4700
|
+
diff: openCodeChange(state),
|
|
4701
|
+
attachments: attachments(key, state.attachments)
|
|
4893
4702
|
}
|
|
4894
4703
|
)
|
|
4895
4704
|
];
|
|
@@ -4925,17 +4734,22 @@ function databaseMessages(path, session) {
|
|
|
4925
4734
|
}
|
|
4926
4735
|
}
|
|
4927
4736
|
function codexSubagent(row) {
|
|
4928
|
-
const
|
|
4929
|
-
const task = typeof
|
|
4737
|
+
const spawn6 = object(object(parse(String(row.source ?? "")))?.subagent)?.thread_spawn;
|
|
4738
|
+
const task = typeof spawn6?.agent_path === "string" ? spawn6.agent_path.split("/").pop() : "";
|
|
4930
4739
|
return [row.agent_nickname, task?.replace(/_/g, " ")].filter(Boolean).join(" \xB7 ");
|
|
4931
4740
|
}
|
|
4741
|
+
var codexState;
|
|
4932
4742
|
function codexTitles(root) {
|
|
4933
4743
|
const titles = /* @__PURE__ */ new Map();
|
|
4934
|
-
const state = (
|
|
4744
|
+
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
4745
|
if (!state) return titles;
|
|
4746
|
+
const path = join2(root, state);
|
|
4747
|
+
const stamp = [path, `${path}-wal`].map((file) => statSync(file, { throwIfNoEntry: false })).map((found) => found ? `${found.ino}:${found.size}:${found.mtimeMs}` : "").join("|");
|
|
4748
|
+
if (codexState?.stamp === stamp) return codexState.titles;
|
|
4749
|
+
codexState = { stamp, titles };
|
|
4936
4750
|
let db;
|
|
4937
4751
|
try {
|
|
4938
|
-
db = openDatabase(
|
|
4752
|
+
db = openDatabase(path);
|
|
4939
4753
|
for (const row of db.prepare("SELECT id, title, first_user_message, source, agent_nickname FROM threads").all()) {
|
|
4940
4754
|
const name = [row.title, row.first_user_message].find(
|
|
4941
4755
|
(value2) => typeof value2 === "string" && !!value2.trim() && !injected(value2)
|
|
@@ -4949,7 +4763,7 @@ function codexTitles(root) {
|
|
|
4949
4763
|
return titles;
|
|
4950
4764
|
}
|
|
4951
4765
|
function openCodeDatabase(path, scan) {
|
|
4952
|
-
if (!
|
|
4766
|
+
if (!existsSync2(path)) {
|
|
4953
4767
|
watchParent(path, scan);
|
|
4954
4768
|
return;
|
|
4955
4769
|
}
|
|
@@ -5011,7 +4825,7 @@ function legacyMessages(root, session) {
|
|
|
5011
4825
|
}
|
|
5012
4826
|
function openCodeLegacy(root, scan) {
|
|
5013
4827
|
const parts2 = join2(root, "part");
|
|
5014
|
-
if (
|
|
4828
|
+
if (existsSync2(parts2)) files(parts2, scan, 1);
|
|
5015
4829
|
for (const path of files(join2(root, "session"), scan).filter((file) => file.endsWith(".json"))) {
|
|
5016
4830
|
try {
|
|
5017
4831
|
const row = jsonFile(path);
|
|
@@ -5064,7 +4878,22 @@ function scanHistories() {
|
|
|
5064
4878
|
}
|
|
5065
4879
|
|
|
5066
4880
|
// src/discovery.ts
|
|
5067
|
-
var
|
|
4881
|
+
var READ_ONLY = {
|
|
4882
|
+
read: true,
|
|
4883
|
+
send: false,
|
|
4884
|
+
queue: false,
|
|
4885
|
+
stop: false,
|
|
4886
|
+
approve: false
|
|
4887
|
+
};
|
|
4888
|
+
var opaqueIds = /* @__PURE__ */ new Map();
|
|
4889
|
+
var opaqueId = (value2, secret) => {
|
|
4890
|
+
const key = `${secret}\0${value2}`;
|
|
4891
|
+
const known = opaqueIds.get(key);
|
|
4892
|
+
if (known) return known;
|
|
4893
|
+
const id = createHmac("sha256", secret).update(value2).digest("hex").slice(0, 32);
|
|
4894
|
+
opaqueIds.set(key, id);
|
|
4895
|
+
return id;
|
|
4896
|
+
};
|
|
5068
4897
|
var agentThread = (harness, session, secret) => opaqueId(`agent\0${harness}\0${session}`, secret);
|
|
5069
4898
|
var zedHost = (value2) => typeof value2 === "string" ? value2 : value2 && typeof value2 === "object" ? String(Object.values(value2)[0] ?? "") : "";
|
|
5070
4899
|
function vscodeHost(authority) {
|
|
@@ -5150,13 +4979,11 @@ function contentText(content) {
|
|
|
5150
4979
|
return typeof part.text === "string" ? part.text : typeof part.Text === "string" ? part.Text : typeof part.value === "string" ? part.value : contentText(part.content);
|
|
5151
4980
|
}
|
|
5152
4981
|
var Discovery = class {
|
|
5153
|
-
constructor(deviceId,
|
|
4982
|
+
constructor(deviceId, secret) {
|
|
5154
4983
|
this.deviceId = deviceId;
|
|
5155
|
-
this.capabilities = capabilities;
|
|
5156
4984
|
this.secret = secret;
|
|
5157
4985
|
}
|
|
5158
4986
|
deviceId;
|
|
5159
|
-
capabilities;
|
|
5160
4987
|
secret;
|
|
5161
4988
|
projects = /* @__PURE__ */ new Map();
|
|
5162
4989
|
locations = /* @__PURE__ */ new Map();
|
|
@@ -5192,7 +5019,7 @@ var Discovery = class {
|
|
|
5192
5019
|
editor: "cli",
|
|
5193
5020
|
status: "unknown",
|
|
5194
5021
|
updatedAt: session.updatedAt,
|
|
5195
|
-
capabilities: { ...
|
|
5022
|
+
capabilities: { ...READ_ONLY },
|
|
5196
5023
|
agent: { harness: session.harness, sessionId: session.id }
|
|
5197
5024
|
};
|
|
5198
5025
|
project.threads.push(thread);
|
|
@@ -5216,7 +5043,7 @@ var Discovery = class {
|
|
|
5216
5043
|
const parent = dirname3(project.path);
|
|
5217
5044
|
counts.set(parent, (counts.get(parent) ?? 0) + 1);
|
|
5218
5045
|
}
|
|
5219
|
-
const folders = [...counts].sort((a, b) => b[1] - a[1]).map(([folder]) => folder).filter((folder) =>
|
|
5046
|
+
const folders = [...counts].sort((a, b) => b[1] - a[1]).map(([folder]) => folder).filter((folder) => existsSync3(folder)).slice(0, 3);
|
|
5220
5047
|
return folders.length ? folders : [homedir3()];
|
|
5221
5048
|
}
|
|
5222
5049
|
observe(session) {
|
|
@@ -5253,7 +5080,7 @@ var Discovery = class {
|
|
|
5253
5080
|
editor: "acp",
|
|
5254
5081
|
status: "idle",
|
|
5255
5082
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5256
|
-
capabilities:
|
|
5083
|
+
capabilities: READ_ONLY
|
|
5257
5084
|
};
|
|
5258
5085
|
project.threads.push(thread);
|
|
5259
5086
|
this.locations.set(id, { editor: "acp", path: "", session });
|
|
@@ -5281,11 +5108,11 @@ var Discovery = class {
|
|
|
5281
5108
|
zed(root) {
|
|
5282
5109
|
const database = join3(root, "db");
|
|
5283
5110
|
const store = join3(root, "threads/threads.db");
|
|
5284
|
-
if (!
|
|
5111
|
+
if (!existsSync3(database)) return;
|
|
5285
5112
|
this.paths.add(database);
|
|
5286
5113
|
for (const entry of readdirSync2(database)) {
|
|
5287
5114
|
const path = join3(database, entry, "db.sqlite");
|
|
5288
|
-
if (!
|
|
5115
|
+
if (!existsSync3(path)) continue;
|
|
5289
5116
|
this.paths.add(join3(database, entry));
|
|
5290
5117
|
try {
|
|
5291
5118
|
if (!sqlite(
|
|
@@ -5294,7 +5121,7 @@ var Discovery = class {
|
|
|
5294
5121
|
).length)
|
|
5295
5122
|
continue;
|
|
5296
5123
|
const stamps = new Map(
|
|
5297
|
-
|
|
5124
|
+
existsSync3(store) ? sqlite(store, "SELECT id, updated_at FROM threads").map((row) => [
|
|
5298
5125
|
row.id,
|
|
5299
5126
|
row.updated_at
|
|
5300
5127
|
]) : []
|
|
@@ -5327,7 +5154,7 @@ var Discovery = class {
|
|
|
5327
5154
|
editor: "zed",
|
|
5328
5155
|
status: "unknown",
|
|
5329
5156
|
updatedAt,
|
|
5330
|
-
capabilities:
|
|
5157
|
+
capabilities: READ_ONLY,
|
|
5331
5158
|
...harness ? { agent: { harness, sessionId: session } } : {}
|
|
5332
5159
|
};
|
|
5333
5160
|
project.threads.push(thread);
|
|
@@ -5342,17 +5169,17 @@ var Discovery = class {
|
|
|
5342
5169
|
}
|
|
5343
5170
|
}
|
|
5344
5171
|
const threads = join3(root, "threads");
|
|
5345
|
-
if (
|
|
5172
|
+
if (existsSync3(threads)) this.paths.add(threads);
|
|
5346
5173
|
}
|
|
5347
5174
|
vscode(root) {
|
|
5348
5175
|
const workspaces = join3(root, "workspaceStorage");
|
|
5349
|
-
if (!
|
|
5176
|
+
if (!existsSync3(workspaces)) return;
|
|
5350
5177
|
this.paths.add(workspaces);
|
|
5351
5178
|
for (const entry of readdirSync2(workspaces)) {
|
|
5352
5179
|
const directory2 = join3(workspaces, entry);
|
|
5353
5180
|
const workspace = join3(directory2, "workspace.json");
|
|
5354
5181
|
const sessions = join3(directory2, "chatSessions");
|
|
5355
|
-
if (!
|
|
5182
|
+
if (!existsSync3(workspace)) continue;
|
|
5356
5183
|
this.paths.add(directory2);
|
|
5357
5184
|
try {
|
|
5358
5185
|
const data = JSON.parse(readFileSync2(workspace, "utf8"));
|
|
@@ -5361,7 +5188,7 @@ var Discovery = class {
|
|
|
5361
5188
|
const path = decodeURIComponent(uri.pathname).replace(/^\/([A-Za-z]:\/)/, "$1");
|
|
5362
5189
|
const host = uri.protocol === "vscode-remote:" ? vscodeHost(uri.host) : "";
|
|
5363
5190
|
const project = this.project(path, host ? { host, path } : void 0);
|
|
5364
|
-
if (!
|
|
5191
|
+
if (!existsSync3(sessions)) continue;
|
|
5365
5192
|
this.paths.add(sessions);
|
|
5366
5193
|
for (const filename of readdirSync2(sessions).filter((name) => /\.jsonl?$/.test(name))) {
|
|
5367
5194
|
const file = join3(sessions, filename);
|
|
@@ -5373,7 +5200,7 @@ var Discovery = class {
|
|
|
5373
5200
|
editor: "vscode",
|
|
5374
5201
|
status: "unknown",
|
|
5375
5202
|
updatedAt: new Date(state.lastMessageDate ?? state.creationDate ?? 0).toISOString(),
|
|
5376
|
-
capabilities:
|
|
5203
|
+
capabilities: READ_ONLY
|
|
5377
5204
|
});
|
|
5378
5205
|
this.locations.set(id, { editor: "vscode", path: file });
|
|
5379
5206
|
}
|
|
@@ -5382,12 +5209,12 @@ var Discovery = class {
|
|
|
5382
5209
|
}
|
|
5383
5210
|
}
|
|
5384
5211
|
}
|
|
5385
|
-
read(thread,
|
|
5212
|
+
read(thread, attached2) {
|
|
5386
5213
|
const location2 = this.locations.get(thread.id);
|
|
5387
5214
|
if (!location2) throw new Error("Thread is no longer available");
|
|
5388
5215
|
if (location2.history) return location2.history.read();
|
|
5389
5216
|
if (location2.live) return location2.live.messages;
|
|
5390
|
-
if (
|
|
5217
|
+
if (attached2) return attached2;
|
|
5391
5218
|
if (location2.editor === "vscode") {
|
|
5392
5219
|
const state = chatSession(location2.path);
|
|
5393
5220
|
return (state.requests ?? []).flatMap((request2, index) => [
|
|
@@ -5399,16 +5226,16 @@ var Discovery = class {
|
|
|
5399
5226
|
}
|
|
5400
5227
|
]).filter((message) => message.text);
|
|
5401
5228
|
}
|
|
5402
|
-
if (!
|
|
5229
|
+
if (!existsSync3(location2.path))
|
|
5403
5230
|
throw new Error(
|
|
5404
|
-
"This external agent keeps its conversation in its own storage.
|
|
5231
|
+
"This external agent keeps its conversation in its own storage. View the open thread on your computer."
|
|
5405
5232
|
);
|
|
5406
5233
|
const db = new DatabaseSync2(location2.path, { readOnly: true });
|
|
5407
5234
|
try {
|
|
5408
5235
|
const row = db.prepare("SELECT data_type, data FROM threads WHERE id = ?").get(location2.session);
|
|
5409
5236
|
if (!row)
|
|
5410
5237
|
throw new Error(
|
|
5411
|
-
"This agent history is not available on this computer.
|
|
5238
|
+
"This agent history is not available on this computer. View the existing session in Zed."
|
|
5412
5239
|
);
|
|
5413
5240
|
const data = row.data_type === "zstd" ? zstdDecompressSync(row.data) : row.data;
|
|
5414
5241
|
const state = JSON.parse(Buffer.from(data).toString("utf8"));
|
|
@@ -10636,20 +10463,20 @@ var deflate$2 = (strm, flush) => {
|
|
|
10636
10463
|
let beg = s.pending;
|
|
10637
10464
|
let left = (s.gzhead.extra.length & 65535) - s.gzindex;
|
|
10638
10465
|
while (s.pending + left > s.pending_buf_size) {
|
|
10639
|
-
let
|
|
10640
|
-
s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex +
|
|
10466
|
+
let copy2 = s.pending_buf_size - s.pending;
|
|
10467
|
+
s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + copy2), s.pending);
|
|
10641
10468
|
s.pending = s.pending_buf_size;
|
|
10642
10469
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
10643
10470
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
10644
10471
|
}
|
|
10645
|
-
s.gzindex +=
|
|
10472
|
+
s.gzindex += copy2;
|
|
10646
10473
|
flush_pending(strm);
|
|
10647
10474
|
if (s.pending !== 0) {
|
|
10648
10475
|
s.last_flush = -1;
|
|
10649
10476
|
return Z_OK$3;
|
|
10650
10477
|
}
|
|
10651
10478
|
beg = 0;
|
|
10652
|
-
left -=
|
|
10479
|
+
left -= copy2;
|
|
10653
10480
|
}
|
|
10654
10481
|
let gzhead_extra = new Uint8Array(s.gzhead.extra);
|
|
10655
10482
|
s.pending_buf.set(gzhead_extra.subarray(s.gzindex, s.gzindex + left), s.pending);
|
|
@@ -11959,7 +11786,7 @@ var fixedtables = (state) => {
|
|
|
11959
11786
|
state.distcode = distfix;
|
|
11960
11787
|
state.distbits = 5;
|
|
11961
11788
|
};
|
|
11962
|
-
var updatewindow = (strm, src, end,
|
|
11789
|
+
var updatewindow = (strm, src, end, copy2) => {
|
|
11963
11790
|
let dist;
|
|
11964
11791
|
const state = strm.state;
|
|
11965
11792
|
if (state.window === null) {
|
|
@@ -11970,20 +11797,20 @@ var updatewindow = (strm, src, end, copy) => {
|
|
|
11970
11797
|
state.wnext = 0;
|
|
11971
11798
|
state.whave = 0;
|
|
11972
11799
|
}
|
|
11973
|
-
if (
|
|
11800
|
+
if (copy2 >= state.wsize) {
|
|
11974
11801
|
state.window.set(src.subarray(end - state.wsize, end), 0);
|
|
11975
11802
|
state.wnext = 0;
|
|
11976
11803
|
state.whave = state.wsize;
|
|
11977
11804
|
} else {
|
|
11978
11805
|
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 =
|
|
11806
|
+
if (dist > copy2) {
|
|
11807
|
+
dist = copy2;
|
|
11808
|
+
}
|
|
11809
|
+
state.window.set(src.subarray(end - copy2, end - copy2 + dist), state.wnext);
|
|
11810
|
+
copy2 -= dist;
|
|
11811
|
+
if (copy2) {
|
|
11812
|
+
state.window.set(src.subarray(end - copy2, end), 0);
|
|
11813
|
+
state.wnext = copy2;
|
|
11987
11814
|
state.whave = state.wsize;
|
|
11988
11815
|
} else {
|
|
11989
11816
|
state.wnext += dist;
|
|
@@ -12006,7 +11833,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
12006
11833
|
let hold;
|
|
12007
11834
|
let bits;
|
|
12008
11835
|
let _in, _out;
|
|
12009
|
-
let
|
|
11836
|
+
let copy2;
|
|
12010
11837
|
let from;
|
|
12011
11838
|
let from_source;
|
|
12012
11839
|
let here = 0;
|
|
@@ -12205,11 +12032,11 @@ var inflate$2 = (strm, flush) => {
|
|
|
12205
12032
|
/* falls through */
|
|
12206
12033
|
case EXTRA:
|
|
12207
12034
|
if (state.flags & 1024) {
|
|
12208
|
-
|
|
12209
|
-
if (
|
|
12210
|
-
|
|
12035
|
+
copy2 = state.length;
|
|
12036
|
+
if (copy2 > have) {
|
|
12037
|
+
copy2 = have;
|
|
12211
12038
|
}
|
|
12212
|
-
if (
|
|
12039
|
+
if (copy2) {
|
|
12213
12040
|
if (state.head) {
|
|
12214
12041
|
len = state.head.extra_len - state.length;
|
|
12215
12042
|
if (!state.head.extra) {
|
|
@@ -12220,18 +12047,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12220
12047
|
next,
|
|
12221
12048
|
// extra field is limited to 65536 bytes
|
|
12222
12049
|
// - no need for additional size check
|
|
12223
|
-
next +
|
|
12050
|
+
next + copy2
|
|
12224
12051
|
),
|
|
12225
12052
|
/*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
|
|
12226
12053
|
len
|
|
12227
12054
|
);
|
|
12228
12055
|
}
|
|
12229
12056
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12230
|
-
state.check = crc32_1(state.check, input,
|
|
12057
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12231
12058
|
}
|
|
12232
|
-
have -=
|
|
12233
|
-
next +=
|
|
12234
|
-
state.length -=
|
|
12059
|
+
have -= copy2;
|
|
12060
|
+
next += copy2;
|
|
12061
|
+
state.length -= copy2;
|
|
12235
12062
|
}
|
|
12236
12063
|
if (state.length) {
|
|
12237
12064
|
break inf_leave;
|
|
@@ -12245,18 +12072,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12245
12072
|
if (have === 0) {
|
|
12246
12073
|
break inf_leave;
|
|
12247
12074
|
}
|
|
12248
|
-
|
|
12075
|
+
copy2 = 0;
|
|
12249
12076
|
do {
|
|
12250
|
-
len = input[next +
|
|
12077
|
+
len = input[next + copy2++];
|
|
12251
12078
|
if (state.head && len && state.length < 65536) {
|
|
12252
12079
|
state.head.name += String.fromCharCode(len);
|
|
12253
12080
|
}
|
|
12254
|
-
} while (len &&
|
|
12081
|
+
} while (len && copy2 < have);
|
|
12255
12082
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12256
|
-
state.check = crc32_1(state.check, input,
|
|
12083
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12257
12084
|
}
|
|
12258
|
-
have -=
|
|
12259
|
-
next +=
|
|
12085
|
+
have -= copy2;
|
|
12086
|
+
next += copy2;
|
|
12260
12087
|
if (len) {
|
|
12261
12088
|
break inf_leave;
|
|
12262
12089
|
}
|
|
@@ -12271,18 +12098,18 @@ var inflate$2 = (strm, flush) => {
|
|
|
12271
12098
|
if (have === 0) {
|
|
12272
12099
|
break inf_leave;
|
|
12273
12100
|
}
|
|
12274
|
-
|
|
12101
|
+
copy2 = 0;
|
|
12275
12102
|
do {
|
|
12276
|
-
len = input[next +
|
|
12103
|
+
len = input[next + copy2++];
|
|
12277
12104
|
if (state.head && len && state.length < 65536) {
|
|
12278
12105
|
state.head.comment += String.fromCharCode(len);
|
|
12279
12106
|
}
|
|
12280
|
-
} while (len &&
|
|
12107
|
+
} while (len && copy2 < have);
|
|
12281
12108
|
if (state.flags & 512 && state.wrap & 4) {
|
|
12282
|
-
state.check = crc32_1(state.check, input,
|
|
12109
|
+
state.check = crc32_1(state.check, input, copy2, next);
|
|
12283
12110
|
}
|
|
12284
|
-
have -=
|
|
12285
|
-
next +=
|
|
12111
|
+
have -= copy2;
|
|
12112
|
+
next += copy2;
|
|
12286
12113
|
if (len) {
|
|
12287
12114
|
break inf_leave;
|
|
12288
12115
|
}
|
|
@@ -12417,23 +12244,23 @@ var inflate$2 = (strm, flush) => {
|
|
|
12417
12244
|
state.mode = COPY;
|
|
12418
12245
|
/* falls through */
|
|
12419
12246
|
case COPY:
|
|
12420
|
-
|
|
12421
|
-
if (
|
|
12422
|
-
if (
|
|
12423
|
-
|
|
12247
|
+
copy2 = state.length;
|
|
12248
|
+
if (copy2) {
|
|
12249
|
+
if (copy2 > have) {
|
|
12250
|
+
copy2 = have;
|
|
12424
12251
|
}
|
|
12425
|
-
if (
|
|
12426
|
-
|
|
12252
|
+
if (copy2 > left) {
|
|
12253
|
+
copy2 = left;
|
|
12427
12254
|
}
|
|
12428
|
-
if (
|
|
12255
|
+
if (copy2 === 0) {
|
|
12429
12256
|
break inf_leave;
|
|
12430
12257
|
}
|
|
12431
|
-
output.set(input.subarray(next, next +
|
|
12432
|
-
have -=
|
|
12433
|
-
next +=
|
|
12434
|
-
left -=
|
|
12435
|
-
put +=
|
|
12436
|
-
state.length -=
|
|
12258
|
+
output.set(input.subarray(next, next + copy2), put);
|
|
12259
|
+
have -= copy2;
|
|
12260
|
+
next += copy2;
|
|
12261
|
+
left -= copy2;
|
|
12262
|
+
put += copy2;
|
|
12263
|
+
state.length -= copy2;
|
|
12437
12264
|
break;
|
|
12438
12265
|
}
|
|
12439
12266
|
state.mode = TYPE;
|
|
@@ -12534,7 +12361,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
12534
12361
|
break;
|
|
12535
12362
|
}
|
|
12536
12363
|
len = state.lens[state.have - 1];
|
|
12537
|
-
|
|
12364
|
+
copy2 = 3 + (hold & 3);
|
|
12538
12365
|
hold >>>= 2;
|
|
12539
12366
|
bits -= 2;
|
|
12540
12367
|
} else if (here_val === 17) {
|
|
@@ -12550,7 +12377,7 @@ var inflate$2 = (strm, flush) => {
|
|
|
12550
12377
|
hold >>>= here_bits;
|
|
12551
12378
|
bits -= here_bits;
|
|
12552
12379
|
len = 0;
|
|
12553
|
-
|
|
12380
|
+
copy2 = 3 + (hold & 7);
|
|
12554
12381
|
hold >>>= 3;
|
|
12555
12382
|
bits -= 3;
|
|
12556
12383
|
} else {
|
|
@@ -12566,16 +12393,16 @@ var inflate$2 = (strm, flush) => {
|
|
|
12566
12393
|
hold >>>= here_bits;
|
|
12567
12394
|
bits -= here_bits;
|
|
12568
12395
|
len = 0;
|
|
12569
|
-
|
|
12396
|
+
copy2 = 11 + (hold & 127);
|
|
12570
12397
|
hold >>>= 7;
|
|
12571
12398
|
bits -= 7;
|
|
12572
12399
|
}
|
|
12573
|
-
if (state.have +
|
|
12400
|
+
if (state.have + copy2 > state.nlen + state.ndist) {
|
|
12574
12401
|
strm.msg = "invalid bit length repeat";
|
|
12575
12402
|
state.mode = BAD;
|
|
12576
12403
|
break;
|
|
12577
12404
|
}
|
|
12578
|
-
while (
|
|
12405
|
+
while (copy2--) {
|
|
12579
12406
|
state.lens[state.have++] = len;
|
|
12580
12407
|
}
|
|
12581
12408
|
}
|
|
@@ -12794,39 +12621,39 @@ var inflate$2 = (strm, flush) => {
|
|
|
12794
12621
|
if (left === 0) {
|
|
12795
12622
|
break inf_leave;
|
|
12796
12623
|
}
|
|
12797
|
-
|
|
12798
|
-
if (state.offset >
|
|
12799
|
-
|
|
12800
|
-
if (
|
|
12624
|
+
copy2 = _out - left;
|
|
12625
|
+
if (state.offset > copy2) {
|
|
12626
|
+
copy2 = state.offset - copy2;
|
|
12627
|
+
if (copy2 > state.whave) {
|
|
12801
12628
|
if (state.sane) {
|
|
12802
12629
|
strm.msg = "invalid distance too far back";
|
|
12803
12630
|
state.mode = BAD;
|
|
12804
12631
|
break;
|
|
12805
12632
|
}
|
|
12806
12633
|
}
|
|
12807
|
-
if (
|
|
12808
|
-
|
|
12809
|
-
from = state.wsize -
|
|
12634
|
+
if (copy2 > state.wnext) {
|
|
12635
|
+
copy2 -= state.wnext;
|
|
12636
|
+
from = state.wsize - copy2;
|
|
12810
12637
|
} else {
|
|
12811
|
-
from = state.wnext -
|
|
12638
|
+
from = state.wnext - copy2;
|
|
12812
12639
|
}
|
|
12813
|
-
if (
|
|
12814
|
-
|
|
12640
|
+
if (copy2 > state.length) {
|
|
12641
|
+
copy2 = state.length;
|
|
12815
12642
|
}
|
|
12816
12643
|
from_source = state.window;
|
|
12817
12644
|
} else {
|
|
12818
12645
|
from_source = output;
|
|
12819
12646
|
from = put - state.offset;
|
|
12820
|
-
|
|
12647
|
+
copy2 = state.length;
|
|
12821
12648
|
}
|
|
12822
|
-
if (
|
|
12823
|
-
|
|
12649
|
+
if (copy2 > left) {
|
|
12650
|
+
copy2 = left;
|
|
12824
12651
|
}
|
|
12825
|
-
left -=
|
|
12826
|
-
state.length -=
|
|
12652
|
+
left -= copy2;
|
|
12653
|
+
state.length -= copy2;
|
|
12827
12654
|
do {
|
|
12828
12655
|
output[put++] = from_source[from++];
|
|
12829
|
-
} while (--
|
|
12656
|
+
} while (--copy2);
|
|
12830
12657
|
if (state.length === 0) {
|
|
12831
12658
|
state.mode = LEN;
|
|
12832
12659
|
}
|
|
@@ -14717,12 +14544,12 @@ var Model = class _Model extends import_index2.default {
|
|
|
14717
14544
|
});
|
|
14718
14545
|
return value2;
|
|
14719
14546
|
} catch {
|
|
14720
|
-
const
|
|
14721
|
-
Object.defineProperty(
|
|
14547
|
+
const copy2 = { ...value2 };
|
|
14548
|
+
Object.defineProperty(copy2, SYM_EXPANDED_REF, {
|
|
14722
14549
|
enumerable: true,
|
|
14723
14550
|
value: true
|
|
14724
14551
|
});
|
|
14725
|
-
return
|
|
14552
|
+
return copy2;
|
|
14726
14553
|
}
|
|
14727
14554
|
}
|
|
14728
14555
|
/**
|
|
@@ -16344,13 +16171,18 @@ import { createHmac as createHmac2, randomBytes as randomBytes3 } from "node:cry
|
|
|
16344
16171
|
var ID = /^[a-f0-9]{64}$/;
|
|
16345
16172
|
var ROLES = new Set(MESSAGE_ROLES);
|
|
16346
16173
|
var ROUTING_FIELDS = ["pairing", "sender", "recipient", "project", "thread"];
|
|
16174
|
+
var MIME = /^[\w.+-]{1,64}\/[\w.+-]{1,64}$/;
|
|
16347
16175
|
var TOOL_TEXT = 500;
|
|
16176
|
+
function validAttachment(value2) {
|
|
16177
|
+
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) && typeof value2.id === "string" && ID.test(value2.id) && typeof value2.mime === "string" && MIME.test(value2.mime) && Number.isSafeInteger(value2.bytes) && value2.bytes >= 0 && (value2.name === void 0 || typeof value2.name === "string" && value2.name.length <= ATTACHMENT_NAME_CHARS);
|
|
16178
|
+
}
|
|
16348
16179
|
function validTool(value2) {
|
|
16349
16180
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) && typeof value2.name === "string" && value2.name.length <= TOOL_TEXT && (value2.summary === void 0 || typeof value2.summary === "string" && value2.summary.length <= TOOL_TEXT) && (value2.status === void 0 || value2.status === "ok" || value2.status === "error") && (value2.ms === void 0 || Number.isSafeInteger(value2.ms) && value2.ms >= 0) && (value2.diff === void 0 || typeof value2.diff === "boolean");
|
|
16350
16181
|
}
|
|
16351
16182
|
function validMessage(value2) {
|
|
16352
|
-
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) && typeof value2.id === "string" && ID.test(value2.id) && ROLES.has(value2.role) && typeof value2.text === "string" && value2.text.length <= HISTORY_TEXT_CHARS && Number.isSafeInteger(value2.part) && Number.isSafeInteger(value2.parts) && value2.part >= 0 && value2.parts > value2.part && (value2.tool === void 0 || value2.part === 0 && validTool(value2.tool));
|
|
16183
|
+
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) && typeof value2.id === "string" && ID.test(value2.id) && ROLES.has(value2.role) && typeof value2.text === "string" && value2.text.length <= HISTORY_TEXT_CHARS && Number.isSafeInteger(value2.part) && Number.isSafeInteger(value2.parts) && value2.part >= 0 && value2.parts > value2.part && (value2.tool === void 0 || value2.part === 0 && validTool(value2.tool)) && (value2.full === void 0 || value2.part === 0 && validAttachment(value2.full)) && (value2.attachments === void 0 || value2.part === 0 && Array.isArray(value2.attachments) && value2.attachments.length <= HISTORY_ATTACHMENTS && value2.attachments.every(validAttachment));
|
|
16353
16184
|
}
|
|
16185
|
+
var preview = (text3) => text3.split("\n", OUTPUT_PREVIEW.lines).join("\n").slice(0, OUTPUT_PREVIEW.chars);
|
|
16354
16186
|
function splitHistoryMessages(messages5, opaqueId2) {
|
|
16355
16187
|
return messages5.flatMap((message) => {
|
|
16356
16188
|
const text3 = [];
|
|
@@ -16369,7 +16201,9 @@ function splitHistoryMessages(messages5, opaqueId2) {
|
|
|
16369
16201
|
text: part,
|
|
16370
16202
|
part: index,
|
|
16371
16203
|
parts: text3.length,
|
|
16372
|
-
...index === 0 && message.tool ? { tool: message.tool } : {}
|
|
16204
|
+
...index === 0 && message.tool ? { tool: message.tool } : {},
|
|
16205
|
+
...index === 0 && message.full ? { full: message.full } : {},
|
|
16206
|
+
...index === 0 && message.attachments?.length ? { attachments: message.attachments } : {}
|
|
16373
16207
|
}));
|
|
16374
16208
|
});
|
|
16375
16209
|
}
|
|
@@ -16410,6 +16244,37 @@ function sealHistory(key, routing, message, random) {
|
|
|
16410
16244
|
// src/history.ts
|
|
16411
16245
|
var HISTORY_PATH = "/dashup/history";
|
|
16412
16246
|
var HISTORY_KEPT = 300;
|
|
16247
|
+
var opaque = (key, value2) => createHmac2("sha256", Buffer.from(key, "hex")).update(JSON.stringify(value2)).digest("hex");
|
|
16248
|
+
function historyMessages(key, project, thread, messages5) {
|
|
16249
|
+
const name = (attachment) => named(key, project, thread, attachment);
|
|
16250
|
+
return splitHistoryMessages(
|
|
16251
|
+
messages5.map((message) => {
|
|
16252
|
+
const { full, attachments: attachments2, ...rest } = kept(message);
|
|
16253
|
+
const files2 = attachments2?.map(name).filter(validAttachment).slice(0, HISTORY_ATTACHMENTS);
|
|
16254
|
+
return {
|
|
16255
|
+
...rest,
|
|
16256
|
+
...full ? { full: name(full) } : {},
|
|
16257
|
+
...files2?.length ? { attachments: files2 } : {}
|
|
16258
|
+
};
|
|
16259
|
+
}),
|
|
16260
|
+
(id) => opaque(key, ["history-message", project, thread, id])
|
|
16261
|
+
);
|
|
16262
|
+
}
|
|
16263
|
+
var copy = (messages5) => new Map(
|
|
16264
|
+
messages5.slice(-HISTORY_PAGE_ITEMS).map((message) => [message.id, JSON.stringify(message)])
|
|
16265
|
+
);
|
|
16266
|
+
function changes(copy2, messages5) {
|
|
16267
|
+
const latest = messages5.slice(-HISTORY_PAGE_ITEMS);
|
|
16268
|
+
let known = latest.length;
|
|
16269
|
+
while (known > 0 && !copy2.has(latest[known - 1].id)) known--;
|
|
16270
|
+
if (!known && copy2.size) return [];
|
|
16271
|
+
return latest.filter((message, at) => {
|
|
16272
|
+
const json = JSON.stringify(message);
|
|
16273
|
+
if (copy2.get(message.id) === json || at < known && !copy2.has(message.id)) return false;
|
|
16274
|
+
copy2.set(message.id, json);
|
|
16275
|
+
return true;
|
|
16276
|
+
});
|
|
16277
|
+
}
|
|
16413
16278
|
async function upload(config2, batch) {
|
|
16414
16279
|
if (batch.records.length > HISTORY_BATCH_ITEMS || Buffer.byteLength(JSON.stringify(batch)) > HISTORY_BATCH_BYTES)
|
|
16415
16280
|
throw new Error("The encrypted history upload exceeds the batch limit");
|
|
@@ -16424,15 +16289,10 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16424
16289
|
const local = config2.pairs[pair.id];
|
|
16425
16290
|
const generation = ++local.sequence;
|
|
16426
16291
|
saveConfig(config2);
|
|
16427
|
-
const
|
|
16428
|
-
const named = (id) => opaque(["history-message", project, thread.id, id]);
|
|
16429
|
-
const all = splitHistoryMessages(messages5, named);
|
|
16292
|
+
const all = historyMessages(local.key, project, thread.id, messages5);
|
|
16430
16293
|
let start = Math.max(0, all.length - HISTORY_KEPT);
|
|
16431
16294
|
while (start < all.length && (all[start].part ?? 0) > 0) start++;
|
|
16432
|
-
const fragments = start ? [
|
|
16433
|
-
...splitHistoryMessages([EARLIER], named),
|
|
16434
|
-
...all.slice(start)
|
|
16435
|
-
] : all;
|
|
16295
|
+
const fragments = start ? [...historyMessages(local.key, project, thread.id, [EARLIER]), ...all.slice(start)] : all;
|
|
16436
16296
|
const summary = { generation, total: fragments.length, updatedAt: thread.updatedAt };
|
|
16437
16297
|
const batch = {
|
|
16438
16298
|
pairing: pair.id,
|
|
@@ -16448,7 +16308,7 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16448
16308
|
const record = sealHistory(
|
|
16449
16309
|
local.key,
|
|
16450
16310
|
{
|
|
16451
|
-
id: opaque(["history-record", project, thread.id, generation, position]),
|
|
16311
|
+
id: opaque(local.key, ["history-record", project, thread.id, generation, position]),
|
|
16452
16312
|
pairing: pair.id,
|
|
16453
16313
|
sender: pair.device,
|
|
16454
16314
|
recipient: pair.mobile,
|
|
@@ -16475,18 +16335,63 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16475
16335
|
throw new Error("The encrypted history snapshot was not published");
|
|
16476
16336
|
return summary;
|
|
16477
16337
|
}
|
|
16338
|
+
var ATTACHMENTS_PATH = "/dashup/attachments";
|
|
16339
|
+
var PASTED = new RegExp(
|
|
16340
|
+
`data:([\\w.+-]+/[\\w.+-]+);base64,([A-Za-z0-9+/]{${OUTPUT_PREVIEW.chars},}={0,2})`,
|
|
16341
|
+
"g"
|
|
16342
|
+
);
|
|
16343
|
+
function kept(message) {
|
|
16344
|
+
const files2 = [...message.attachments ?? []];
|
|
16345
|
+
const text3 = message.role === "tool" ? message.text : message.text.replace(PASTED, (_, mime, data, at) => {
|
|
16346
|
+
files2.push(attached(`${message.id}@${at}`, mime, data));
|
|
16347
|
+
return "";
|
|
16348
|
+
});
|
|
16349
|
+
const start = message.role === "tool" && !message.tool?.diff ? preview(text3) : text3;
|
|
16350
|
+
const full = start.length < text3.length ? {
|
|
16351
|
+
id: `${message.id}:full`,
|
|
16352
|
+
mime: "text/plain",
|
|
16353
|
+
bytes: Buffer.byteLength(text3),
|
|
16354
|
+
read: () => Buffer.from(text3)
|
|
16355
|
+
} : void 0;
|
|
16356
|
+
return {
|
|
16357
|
+
...message,
|
|
16358
|
+
text: start,
|
|
16359
|
+
...full ? { full } : {},
|
|
16360
|
+
...files2.length ? { attachments: files2 } : {}
|
|
16361
|
+
};
|
|
16362
|
+
}
|
|
16363
|
+
var named = (key, project, thread, attachment) => ({
|
|
16364
|
+
id: opaque(key, ["history-attachment", project, thread, attachment.id, attachment.bytes]),
|
|
16365
|
+
mime: attachment.mime,
|
|
16366
|
+
bytes: attachment.bytes,
|
|
16367
|
+
...attachment.name ? { name: attachment.name } : {}
|
|
16368
|
+
});
|
|
16369
|
+
var readable = (attachment) => "read" in attachment;
|
|
16370
|
+
async function shareAttachment(config2, pair, project, thread, messages5, id) {
|
|
16371
|
+
if (!activePair(config2, pair)) throw new Error("Pairing is no longer available");
|
|
16372
|
+
const key = config2.pairs[pair.id].key;
|
|
16373
|
+
const found = messages5.flatMap((message) => {
|
|
16374
|
+
const { full, attachments: attachments2 = [] } = kept(message);
|
|
16375
|
+
return full ? [full, ...attachments2] : attachments2;
|
|
16376
|
+
}).find((attachment) => named(key, project, thread, attachment).id === id);
|
|
16377
|
+
if (!found || !readable(found)) throw new Error("This attachment is no longer on your computer");
|
|
16378
|
+
if (found.bytes > MAX_ATTACHMENT_BYTES)
|
|
16379
|
+
throw new Error("This attachment is too large to open on your phone");
|
|
16380
|
+
const sealed = sealAttachment(key, pair.id, id, found.read(), randomBytes3);
|
|
16381
|
+
await api(config2, `${ATTACHMENTS_PATH}/${pair.id}/${id}`, sealed);
|
|
16382
|
+
}
|
|
16478
16383
|
|
|
16479
16384
|
// src/harness/live.ts
|
|
16480
|
-
import { promisify as
|
|
16481
|
-
import { execFile as
|
|
16385
|
+
import { promisify as promisify5 } from "node:util";
|
|
16386
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
16482
16387
|
|
|
16483
16388
|
// src/harness/codex.ts
|
|
16484
16389
|
import WebSocket2 from "ws";
|
|
16485
16390
|
import { homedir as homedir4 } from "node:os";
|
|
16486
16391
|
import { connect } from "node:net";
|
|
16487
|
-
import { promisify as
|
|
16392
|
+
import { promisify as promisify2 } from "node:util";
|
|
16488
16393
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
16489
|
-
import { execFile as
|
|
16394
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
16490
16395
|
import { join as join4, isAbsolute as isAbsolute2 } from "node:path";
|
|
16491
16396
|
import { stat, readdir, readFile } from "node:fs/promises";
|
|
16492
16397
|
var REQUEST_MS = 4e3;
|
|
@@ -16510,7 +16415,7 @@ function approval(id, method, params) {
|
|
|
16510
16415
|
const oversized = Buffer.byteLength(detail) > MAX_PREVIEW;
|
|
16511
16416
|
const unavailable = !command2 || params.kind !== void 0 && params.kind !== "command" || typeof params.command !== "string" || !params.command.trim() || oversized;
|
|
16512
16417
|
if (oversized) detail = Buffer.from(detail).subarray(0, MAX_PREVIEW).toString("utf8");
|
|
16513
|
-
if (unavailable) detail = [detail,
|
|
16418
|
+
if (unavailable) detail = [detail, NO_PREVIEW].filter(Boolean).join("\n\n");
|
|
16514
16419
|
return {
|
|
16515
16420
|
id,
|
|
16516
16421
|
title: command2 ? "Allow command?" : "Allow file changes?",
|
|
@@ -16806,7 +16711,7 @@ var Codex = class {
|
|
|
16806
16711
|
// Codex's own daemon runs the threads Dashup starts or resumes, so they outlive this connector.
|
|
16807
16712
|
async start(cwd, text3, saved) {
|
|
16808
16713
|
const path = process.env.DASHUP_CODEX_SOCKET || join4(codexHome(), ...SOCKET_PARTS);
|
|
16809
|
-
await
|
|
16714
|
+
await promisify2(execFile2)("codex", ["app-server", "daemon", "start"], {
|
|
16810
16715
|
timeout: 6e4,
|
|
16811
16716
|
shell: process.platform === "win32"
|
|
16812
16717
|
});
|
|
@@ -16849,8 +16754,8 @@ var Codex = class {
|
|
|
16849
16754
|
// src/harness/headless.ts
|
|
16850
16755
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
16851
16756
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
16852
|
-
import { createInterface as
|
|
16853
|
-
import { spawn as
|
|
16757
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
16758
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
16854
16759
|
var CHOICES = [
|
|
16855
16760
|
{ id: "accept", label: "Allow once" },
|
|
16856
16761
|
{ id: "decline", label: "Reject" }
|
|
@@ -16973,7 +16878,7 @@ var Session = class {
|
|
|
16973
16878
|
this.changed();
|
|
16974
16879
|
return Promise.resolve();
|
|
16975
16880
|
}
|
|
16976
|
-
const turn = this.turn =
|
|
16881
|
+
const turn = this.turn = spawn2(
|
|
16977
16882
|
"claude",
|
|
16978
16883
|
[
|
|
16979
16884
|
"-p",
|
|
@@ -16996,7 +16901,7 @@ var Session = class {
|
|
|
16996
16901
|
errors = (errors + String(chunk)).slice(-2e3);
|
|
16997
16902
|
});
|
|
16998
16903
|
const started = new Promise((resolve3, reject) => {
|
|
16999
|
-
|
|
16904
|
+
createInterface2({ input: turn.stdout }).on("line", (line) => {
|
|
17000
16905
|
if (this.receive(line)) resolve3();
|
|
17001
16906
|
});
|
|
17002
16907
|
const end = (reason) => {
|
|
@@ -17028,7 +16933,7 @@ var Session = class {
|
|
|
17028
16933
|
else if (message.type === "assistant") {
|
|
17029
16934
|
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
16935
|
if (!text3) return false;
|
|
17031
|
-
this.messages.push({ id: String(message.
|
|
16936
|
+
this.messages.push({ id: String(message.uuid ?? randomUUID3()), role: "assistant", text: text3 });
|
|
17032
16937
|
} else if (message.type === "control_request" && message.request?.subtype === "can_use_tool") {
|
|
17033
16938
|
const id = randomUUID3();
|
|
17034
16939
|
this.approvals.set(id, {
|
|
@@ -17250,9 +17155,9 @@ var Claude = class {
|
|
|
17250
17155
|
import Socket3 from "ws";
|
|
17251
17156
|
import { homedir as homedir5 } from "node:os";
|
|
17252
17157
|
import { connect as connect3 } from "node:net";
|
|
17253
|
-
import { promisify as
|
|
17254
|
-
import { fileURLToPath
|
|
17255
|
-
import { execFile as
|
|
17158
|
+
import { promisify as promisify3 } from "node:util";
|
|
17159
|
+
import { fileURLToPath } from "node:url";
|
|
17160
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
17256
17161
|
import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
|
|
17257
17162
|
import { stat as stat2, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
17258
17163
|
import { join as join6, dirname as dirname4, basename as basename2, isAbsolute as isAbsolute3 } from "node:path";
|
|
@@ -17265,7 +17170,7 @@ var MAX_METADATA = 64 * 1024;
|
|
|
17265
17170
|
var MAX_HOSTS = 16;
|
|
17266
17171
|
var MAX_SESSIONS3 = 100;
|
|
17267
17172
|
var ROOT_CHANNEL = "ahp-root://";
|
|
17268
|
-
var
|
|
17173
|
+
var run = promisify3(execFile3);
|
|
17269
17174
|
var CONFIRMATION_CHOICES = [
|
|
17270
17175
|
{ id: "approve", label: "Allow once", kind: "approve" },
|
|
17271
17176
|
{ id: "deny", label: "Reject", kind: "deny" }
|
|
@@ -17285,7 +17190,7 @@ async function privateFile(path, limit) {
|
|
|
17285
17190
|
const encodedPath = Buffer.from(path).toString("base64");
|
|
17286
17191
|
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
17192
|
try {
|
|
17288
|
-
const { stdout } = await
|
|
17193
|
+
const { stdout } = await run(
|
|
17289
17194
|
"powershell.exe",
|
|
17290
17195
|
[
|
|
17291
17196
|
"-NoProfile",
|
|
@@ -17354,7 +17259,7 @@ function directory(state) {
|
|
|
17354
17259
|
const uri = state.workingDirectories?.[0];
|
|
17355
17260
|
if (typeof uri !== "string" || !uri.startsWith("file:")) return;
|
|
17356
17261
|
try {
|
|
17357
|
-
return
|
|
17262
|
+
return fileURLToPath(uri);
|
|
17358
17263
|
} catch {
|
|
17359
17264
|
return;
|
|
17360
17265
|
}
|
|
@@ -17375,7 +17280,7 @@ function confirmation(toolCall) {
|
|
|
17375
17280
|
const paths = [before, after].filter((uri) => typeof uri === "string");
|
|
17376
17281
|
const names = [...new Set(paths)].map((uri) => {
|
|
17377
17282
|
try {
|
|
17378
|
-
return
|
|
17283
|
+
return fileURLToPath(uri);
|
|
17379
17284
|
} catch {
|
|
17380
17285
|
return uri;
|
|
17381
17286
|
}
|
|
@@ -17402,8 +17307,7 @@ function confirmation(toolCall) {
|
|
|
17402
17307
|
})) : [];
|
|
17403
17308
|
if (unavailable) choices = choices.filter((choice) => choice.kind === "deny");
|
|
17404
17309
|
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");
|
|
17310
|
+
if (unavailable || !choices.length) detail = [detail, NO_PREVIEW].filter(Boolean).join("\n\n");
|
|
17407
17311
|
return { detail, choices };
|
|
17408
17312
|
}
|
|
17409
17313
|
function messages3(chat) {
|
|
@@ -17628,12 +17532,12 @@ var Host = class {
|
|
|
17628
17532
|
for (const pending of Array.isArray(session.inputNeeded) ? session.inputNeeded : []) {
|
|
17629
17533
|
if (pending.kind !== "toolConfirmation" || pending.chat !== item.resource || pending.toolCall?.status !== "pending-confirmation")
|
|
17630
17534
|
continue;
|
|
17631
|
-
const
|
|
17535
|
+
const preview2 = confirmation(pending.toolCall);
|
|
17632
17536
|
approvals.push({
|
|
17633
17537
|
id: pending.id,
|
|
17634
17538
|
title: text2(pending.toolCall.confirmationTitle) || pending.toolCall.displayName || "Native agent approval",
|
|
17635
|
-
detail:
|
|
17636
|
-
choices:
|
|
17539
|
+
detail: preview2.detail,
|
|
17540
|
+
choices: preview2.choices.map(({ id: id2, label }) => ({ id: id2, label }))
|
|
17637
17541
|
});
|
|
17638
17542
|
}
|
|
17639
17543
|
const id = `vscode-ahp:${createHash3("sha256").update(this.entry.root).digest("hex").slice(0, 16)}:${item.resource}`;
|
|
@@ -17747,11 +17651,11 @@ var VSCode = class {
|
|
|
17747
17651
|
|
|
17748
17652
|
// src/harness/opencode.ts
|
|
17749
17653
|
import { join as join7 } from "node:path";
|
|
17750
|
-
import { promisify as
|
|
17654
|
+
import { promisify as promisify4 } from "node:util";
|
|
17751
17655
|
import { createHash as createHash4 } from "node:crypto";
|
|
17752
|
-
import { execFile as
|
|
17656
|
+
import { execFile as execFile4, spawn as spawn3 } from "node:child_process";
|
|
17753
17657
|
import { stat as stat3, readlink, readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
|
|
17754
|
-
var
|
|
17658
|
+
var run2 = promisify4(execFile4);
|
|
17755
17659
|
var REQUEST_MS4 = 4e3;
|
|
17756
17660
|
var MAX_BYTES3 = 8 * 1024 * 1024;
|
|
17757
17661
|
var MAX_SESSIONS4 = 100;
|
|
@@ -17831,7 +17735,7 @@ async function endpoints() {
|
|
|
17831
17735
|
if (process.env.DASHUP_LIVE_DISCOVERY === "0") return [];
|
|
17832
17736
|
if (process.platform === "linux") return linuxEndpoints();
|
|
17833
17737
|
if (process.platform === "darwin") {
|
|
17834
|
-
const { stdout } = await
|
|
17738
|
+
const { stdout } = await run2(
|
|
17835
17739
|
"lsof",
|
|
17836
17740
|
["-nP", "-a", "-c", "opencode", "-iTCP", "-sTCP:LISTEN", "-Fn"],
|
|
17837
17741
|
{ timeout: REQUEST_MS4, maxBuffer: MAX_BYTES3 }
|
|
@@ -17848,7 +17752,7 @@ async function endpoints() {
|
|
|
17848
17752
|
}
|
|
17849
17753
|
if (process.platform === "win32") {
|
|
17850
17754
|
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
|
|
17755
|
+
const { stdout } = await run2(
|
|
17852
17756
|
"powershell.exe",
|
|
17853
17757
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
17854
17758
|
{ timeout: REQUEST_MS4, maxBuffer: MAX_BYTES3 }
|
|
@@ -17929,7 +17833,7 @@ ${always.join("\n")}` : ""
|
|
|
17929
17833
|
}
|
|
17930
17834
|
if (unavailable) detail += `
|
|
17931
17835
|
|
|
17932
|
-
${
|
|
17836
|
+
${NO_PREVIEW}`;
|
|
17933
17837
|
return {
|
|
17934
17838
|
id: permission.id,
|
|
17935
17839
|
title: `Allow ${String(permission.permission ?? "tool")}?`,
|
|
@@ -18130,7 +18034,7 @@ var Server = class {
|
|
|
18130
18034
|
if (!permissionApproval(permission, history2, cwd).choices.some(
|
|
18131
18035
|
(value2) => value2.id === choice
|
|
18132
18036
|
))
|
|
18133
|
-
throw new Error(
|
|
18037
|
+
throw new Error(NO_PREVIEW);
|
|
18134
18038
|
}
|
|
18135
18039
|
await this.request(`/permission/${encodeURIComponent(approvalId)}/reply`, cwd, {
|
|
18136
18040
|
reply: choice
|
|
@@ -18217,7 +18121,7 @@ var Server = class {
|
|
|
18217
18121
|
}
|
|
18218
18122
|
};
|
|
18219
18123
|
function serve(cwd) {
|
|
18220
|
-
const child =
|
|
18124
|
+
const child = spawn3("opencode", ["serve", "--hostname", "127.0.0.1", "--port", "0"], {
|
|
18221
18125
|
cwd,
|
|
18222
18126
|
stdio: ["ignore", "pipe", "ignore"],
|
|
18223
18127
|
shell: process.platform === "win32"
|
|
@@ -18342,7 +18246,7 @@ var LiveHarnesses = class {
|
|
|
18342
18246
|
async agents() {
|
|
18343
18247
|
const harnesses = ["claude", "codex", "opencode"];
|
|
18344
18248
|
const runs = await Promise.all(
|
|
18345
|
-
harnesses.map((harness) =>
|
|
18249
|
+
harnesses.map((harness) => promisify5(execFile5)(harness, ["--version"], {
|
|
18346
18250
|
timeout: 1e4,
|
|
18347
18251
|
shell: process.platform === "win32"
|
|
18348
18252
|
}).then(
|
|
@@ -18395,10 +18299,10 @@ var LiveHarnesses = class {
|
|
|
18395
18299
|
|
|
18396
18300
|
// src/harness/queue.ts
|
|
18397
18301
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18398
|
-
import { spawn as
|
|
18302
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
18399
18303
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
18400
18304
|
import { join as join8, dirname as dirname5, basename as basename3 } from "node:path";
|
|
18401
|
-
import { statSync as statSync2, existsSync as
|
|
18305
|
+
import { statSync as statSync2, existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync4, readlinkSync, realpathSync as realpathSync2 } from "node:fs";
|
|
18402
18306
|
var CODEX_VERSION = "0.160.1";
|
|
18403
18307
|
var REQUEST_MS5 = 1e4;
|
|
18404
18308
|
var MAX_BYTES4 = 1024 * 1024;
|
|
@@ -18419,7 +18323,7 @@ function packageVersion(executable) {
|
|
|
18419
18323
|
let directory2 = dirname5(executable);
|
|
18420
18324
|
for (let depth = 0; depth < 8; depth++, directory2 = dirname5(directory2)) {
|
|
18421
18325
|
const path = join8(directory2, "package.json");
|
|
18422
|
-
if (!
|
|
18326
|
+
if (!existsSync4(path)) continue;
|
|
18423
18327
|
const value2 = JSON.parse(readFileSync4(path, "utf8"));
|
|
18424
18328
|
if (value2.name?.startsWith("@openai/codex-")) return value2.version;
|
|
18425
18329
|
}
|
|
@@ -18520,7 +18424,7 @@ function validateEvidence(owner) {
|
|
|
18520
18424
|
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
18425
|
throw new Error("The original native history storage changed");
|
|
18522
18426
|
const path = join8(owner.home, "thread_history_1.sqlite");
|
|
18523
|
-
if (
|
|
18427
|
+
if (existsSync4(path) && identity(path).path !== path)
|
|
18524
18428
|
throw new Error("Native history is outside the original home");
|
|
18525
18429
|
}
|
|
18526
18430
|
function nativeReceipt(owner, clientId) {
|
|
@@ -18537,7 +18441,7 @@ function nativeReceipt(owner, clientId) {
|
|
|
18537
18441
|
queue.close();
|
|
18538
18442
|
}
|
|
18539
18443
|
const path = join8(owner.home, "thread_history_1.sqlite");
|
|
18540
|
-
if (!
|
|
18444
|
+
if (!existsSync4(path)) return { queueId };
|
|
18541
18445
|
const history = new DatabaseSync3(path, { readOnly: true });
|
|
18542
18446
|
try {
|
|
18543
18447
|
const rows = history.prepare(
|
|
@@ -18592,7 +18496,7 @@ var Writer = class {
|
|
|
18592
18496
|
tail = "";
|
|
18593
18497
|
pending = /* @__PURE__ */ new Map();
|
|
18594
18498
|
constructor(owner) {
|
|
18595
|
-
this.process =
|
|
18499
|
+
this.process = spawn4(owner.executable, ["app-server", "--listen", "stdio://"], {
|
|
18596
18500
|
cwd: owner.cwd,
|
|
18597
18501
|
env: { ...process.env, CODEX_HOME: owner.home },
|
|
18598
18502
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -18751,6 +18655,8 @@ var NativeQueue = class {
|
|
|
18751
18655
|
// src/connector.ts
|
|
18752
18656
|
var EPHEMERAL_TTL_MS = 6e4;
|
|
18753
18657
|
var HOME_RECENT = 20;
|
|
18658
|
+
var FOLLOW_MS = 5 * 6e4;
|
|
18659
|
+
var RESCAN_MS = 1e3;
|
|
18754
18660
|
var Command = z2.object({
|
|
18755
18661
|
type: z2.literal("command"),
|
|
18756
18662
|
requestId: z2.string().min(1),
|
|
@@ -18763,8 +18669,7 @@ var Command = z2.object({
|
|
|
18763
18669
|
z2.object({ kind: z2.literal("thread.queue"), text: z2.string().min(1).max(1e5) }).strict(),
|
|
18764
18670
|
z2.object({ kind: z2.literal("thread.stop") }).strict(),
|
|
18765
18671
|
z2.object({ kind: z2.literal("approval.respond"), approvalId: z2.string(), choice: z2.string() }).strict(),
|
|
18766
|
-
z2.object({ kind: z2.literal("
|
|
18767
|
-
z2.object({ kind: z2.literal("screen.stop"), sessionId: z2.string().min(1).max(128) }).strict(),
|
|
18672
|
+
z2.object({ kind: z2.literal("attachment.read"), id: z2.string().regex(/^[a-f0-9]{64}$/) }).strict(),
|
|
18768
18673
|
z2.object({
|
|
18769
18674
|
kind: z2.literal("session.start"),
|
|
18770
18675
|
harness: z2.enum(["claude", "codex", "opencode"]),
|
|
@@ -18790,31 +18695,13 @@ var Connector = class {
|
|
|
18790
18695
|
autoConnect: false
|
|
18791
18696
|
});
|
|
18792
18697
|
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
|
-
);
|
|
18698
|
+
this.discovery = new Discovery(config2.device.id, config2.localSecret);
|
|
18811
18699
|
this.pairings = createLiveQuery(() => this.client.bind(Pairing).where({ device: config2.device.id }).whereNull("revokedAt").clearLimit());
|
|
18812
18700
|
this.relays = createLiveQuery(() => this.client.bind(Relay).where({ recipient: config2.device.id }).orderBy("createdAt", "desc").orderBy("id", "desc").clearLimit());
|
|
18813
18701
|
}
|
|
18814
18702
|
config;
|
|
18815
18703
|
client;
|
|
18816
18704
|
discovery;
|
|
18817
|
-
viewer;
|
|
18818
18705
|
pairings;
|
|
18819
18706
|
relays;
|
|
18820
18707
|
nativeQueue;
|
|
@@ -18825,8 +18712,17 @@ var Connector = class {
|
|
|
18825
18712
|
retry;
|
|
18826
18713
|
stopWatching = () => {
|
|
18827
18714
|
};
|
|
18828
|
-
|
|
18829
|
-
|
|
18715
|
+
rescanning;
|
|
18716
|
+
stopped = false;
|
|
18717
|
+
rescan = () => {
|
|
18718
|
+
if (this.rescanning) return;
|
|
18719
|
+
this.rescanning = setTimeout(() => {
|
|
18720
|
+
this.rescanning = void 0;
|
|
18721
|
+
void this.queueChange().catch(() => {
|
|
18722
|
+
});
|
|
18723
|
+
}, RESCAN_MS);
|
|
18724
|
+
this.rescanning.unref();
|
|
18725
|
+
};
|
|
18830
18726
|
releases = [];
|
|
18831
18727
|
announced = /* @__PURE__ */ new Set();
|
|
18832
18728
|
listed = /* @__PURE__ */ new Map();
|
|
@@ -18838,6 +18734,8 @@ var Connector = class {
|
|
|
18838
18734
|
snapshots = /* @__PURE__ */ new Set();
|
|
18839
18735
|
nativeSnapshots = /* @__PURE__ */ new Map();
|
|
18840
18736
|
queueReceipts = /* @__PURE__ */ new Map();
|
|
18737
|
+
// The conversations phones are reading, by pairing and thread.
|
|
18738
|
+
follows = /* @__PURE__ */ new Map();
|
|
18841
18739
|
// The content key behind each pairing code on show, until a phone pairs with it.
|
|
18842
18740
|
invitations = /* @__PURE__ */ new Map();
|
|
18843
18741
|
async start() {
|
|
@@ -18854,8 +18752,6 @@ var Connector = class {
|
|
|
18854
18752
|
this.releases.push(this.harnesses.watch(this.rescan));
|
|
18855
18753
|
this.releases.push(
|
|
18856
18754
|
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
18755
|
this.work = this.work.then(async () => {
|
|
18860
18756
|
if (this.pairings.snapshot().status !== "ready") return;
|
|
18861
18757
|
for (const pair of this.activePairs()) {
|
|
@@ -18935,7 +18831,6 @@ var Connector = class {
|
|
|
18935
18831
|
this.client.send("dashup:presence", { device: this.config.device.id });
|
|
18936
18832
|
});
|
|
18937
18833
|
this.client.on("error", () => this.report(new Error("Connection interrupted. Dashup will reconnect.")));
|
|
18938
|
-
this.client.on("disconnected", () => this.viewer.close());
|
|
18939
18834
|
await this.client.reconnect();
|
|
18940
18835
|
process.stdout.write(
|
|
18941
18836
|
`Dashup is connected. ${this.discovery.projects.size} existing projects found.
|
|
@@ -18949,29 +18844,16 @@ var Connector = class {
|
|
|
18949
18844
|
return invite;
|
|
18950
18845
|
}
|
|
18951
18846
|
stop() {
|
|
18847
|
+
this.stopped = true;
|
|
18952
18848
|
this.stopWatching();
|
|
18953
18849
|
for (const release of this.releases.splice(0)) release();
|
|
18954
18850
|
clearTimeout(this.retry);
|
|
18851
|
+
clearTimeout(this.rescanning);
|
|
18955
18852
|
this.client.dispose();
|
|
18956
|
-
this.viewer.close();
|
|
18957
18853
|
this.nativeQueue.stop();
|
|
18958
18854
|
this.harnesses.stop();
|
|
18959
18855
|
for (const agent of this.agents.values()) agent.socket.destroy();
|
|
18960
18856
|
}
|
|
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
18857
|
activePairs() {
|
|
18976
18858
|
return this.pairings.snapshot().items;
|
|
18977
18859
|
}
|
|
@@ -19000,7 +18882,7 @@ var Connector = class {
|
|
|
19000
18882
|
);
|
|
19001
18883
|
if (envelope.ciphertext.length > MAX_ENVELOPE_BYTES * 2)
|
|
19002
18884
|
throw new Error(
|
|
19003
|
-
"This update exceeds the encrypted message limit.
|
|
18885
|
+
"This update exceeds the encrypted message limit. View the full conversation on your computer."
|
|
19004
18886
|
);
|
|
19005
18887
|
this.config.outbox.push(envelope);
|
|
19006
18888
|
saveConfig(this.config);
|
|
@@ -19013,7 +18895,7 @@ var Connector = class {
|
|
|
19013
18895
|
saveConfig(this.config);
|
|
19014
18896
|
}
|
|
19015
18897
|
flush() {
|
|
19016
|
-
const
|
|
18898
|
+
const run4 = this.drain.catch(() => {
|
|
19017
18899
|
}).then(async () => {
|
|
19018
18900
|
this.discardLegacySnapshots();
|
|
19019
18901
|
while (this.config.outbox.length && this.client.isConnected) {
|
|
@@ -19023,8 +18905,8 @@ var Connector = class {
|
|
|
19023
18905
|
saveConfig(this.config);
|
|
19024
18906
|
}
|
|
19025
18907
|
});
|
|
19026
|
-
this.drain =
|
|
19027
|
-
return
|
|
18908
|
+
this.drain = run4;
|
|
18909
|
+
return run4.catch((error) => {
|
|
19028
18910
|
this.report(error);
|
|
19029
18911
|
clearTimeout(this.retry);
|
|
19030
18912
|
this.retry = setTimeout(() => {
|
|
@@ -19065,7 +18947,6 @@ var Connector = class {
|
|
|
19065
18947
|
return {
|
|
19066
18948
|
type: "projects",
|
|
19067
18949
|
version,
|
|
19068
|
-
screen: this.discovery.capabilities.screen,
|
|
19069
18950
|
agents: this.startable,
|
|
19070
18951
|
folders: this.discovery.folders(),
|
|
19071
18952
|
projects: [...this.listedProjects.values()],
|
|
@@ -19099,15 +18980,16 @@ var Connector = class {
|
|
|
19099
18980
|
// transcript costs one rescan rather than one each.
|
|
19100
18981
|
queueChange() {
|
|
19101
18982
|
if (this.pendingChange) return this.pendingChange;
|
|
19102
|
-
const
|
|
18983
|
+
const run4 = this.work.then(() => {
|
|
19103
18984
|
this.pendingChange = void 0;
|
|
19104
18985
|
return this.changed();
|
|
19105
18986
|
});
|
|
19106
|
-
this.pendingChange =
|
|
19107
|
-
this.work =
|
|
19108
|
-
return
|
|
18987
|
+
this.pendingChange = run4;
|
|
18988
|
+
this.work = run4.catch((error) => this.report(error));
|
|
18989
|
+
return run4;
|
|
19109
18990
|
}
|
|
19110
18991
|
async changed() {
|
|
18992
|
+
if (this.stopped) return;
|
|
19111
18993
|
const previous = new Map(
|
|
19112
18994
|
[...this.discovery.projects.values()].map((project) => [project.id, project])
|
|
19113
18995
|
);
|
|
@@ -19142,9 +19024,19 @@ var Connector = class {
|
|
|
19142
19024
|
const projects = [...this.discovery.projects.values()];
|
|
19143
19025
|
this.stopWatching();
|
|
19144
19026
|
this.stopWatching = this.discovery.watch(this.rescan);
|
|
19027
|
+
await this.stream();
|
|
19145
19028
|
for (const project of projects) {
|
|
19146
|
-
|
|
19147
|
-
|
|
19029
|
+
const before = previous.get(project.id);
|
|
19030
|
+
const sent = new Map(before?.threads.map((thread) => [thread.id, JSON.stringify(thread)]));
|
|
19031
|
+
const changed = project.threads.filter(
|
|
19032
|
+
(thread) => sent.get(thread.id) !== JSON.stringify(thread)
|
|
19033
|
+
);
|
|
19034
|
+
const whole = !before || project.threads.filter((thread) => sent.has(thread.id)).length < sent.size || JSON.stringify({ ...before, threads: [] }) !== JSON.stringify({ ...project, threads: [] });
|
|
19035
|
+
for (const pair of this.activePairs())
|
|
19036
|
+
if (whole) await this.send(pair, { type: "project", project });
|
|
19037
|
+
else
|
|
19038
|
+
for (const thread of changed)
|
|
19039
|
+
if (!current.has(thread.id)) await this.thread(pair, project, thread);
|
|
19148
19040
|
}
|
|
19149
19041
|
for (const { project, thread, session } of live) {
|
|
19150
19042
|
const snapshot = this.threadState(project, thread);
|
|
@@ -19167,6 +19059,40 @@ var Connector = class {
|
|
|
19167
19059
|
for (const pair of this.activePairs()) await this.listProjects(pair);
|
|
19168
19060
|
await this.publishQueueReceipts();
|
|
19169
19061
|
}
|
|
19062
|
+
// A phone reading a conversation gets its messages as they change, before anything that says the
|
|
19063
|
+
// conversation changed, so it never asks for a copy it already has.
|
|
19064
|
+
async stream() {
|
|
19065
|
+
for (const [key, follow] of this.follows) {
|
|
19066
|
+
const thread = this.discovery.projects.get(follow.project)?.threads.find((item) => item.id === follow.thread);
|
|
19067
|
+
let messages5;
|
|
19068
|
+
try {
|
|
19069
|
+
if (thread && follow.until > Date.now() && activePair(this.config, follow.pair))
|
|
19070
|
+
messages5 = changes(
|
|
19071
|
+
follow.copy,
|
|
19072
|
+
this.discovery.read(thread, this.attached(thread)?.messages)
|
|
19073
|
+
);
|
|
19074
|
+
} catch {
|
|
19075
|
+
}
|
|
19076
|
+
if (!thread || !messages5) {
|
|
19077
|
+
this.follows.delete(key);
|
|
19078
|
+
continue;
|
|
19079
|
+
}
|
|
19080
|
+
if (!messages5.length && thread.updatedAt === follow.updatedAt) continue;
|
|
19081
|
+
follow.updatedAt = thread.updatedAt;
|
|
19082
|
+
await this.send(follow.pair, {
|
|
19083
|
+
type: "thread-tail",
|
|
19084
|
+
projectId: follow.project,
|
|
19085
|
+
threadId: follow.thread,
|
|
19086
|
+
updatedAt: follow.updatedAt,
|
|
19087
|
+
messages: historyMessages(
|
|
19088
|
+
this.config.pairs[follow.pair.id].key,
|
|
19089
|
+
follow.project,
|
|
19090
|
+
follow.thread,
|
|
19091
|
+
messages5
|
|
19092
|
+
)
|
|
19093
|
+
});
|
|
19094
|
+
}
|
|
19095
|
+
}
|
|
19170
19096
|
// A saved session nothing else has open continues headless when its agent runs here. One a
|
|
19171
19097
|
// native process owns takes queued messages instead.
|
|
19172
19098
|
refreshResumes() {
|
|
@@ -19218,38 +19144,23 @@ var Connector = class {
|
|
|
19218
19144
|
}
|
|
19219
19145
|
async receive(envelope) {
|
|
19220
19146
|
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 ||
|
|
19147
|
+
if (!pair || !activePair(this.config, pair) || envelope.sender !== pair.mobile || envelope.recipient !== pair.device || envelope.channel !== "command")
|
|
19222
19148
|
return;
|
|
19223
19149
|
const key = this.config.pairs[pair.id];
|
|
19224
19150
|
if (envelope.sequence <= key.received) return;
|
|
19225
19151
|
const content = open(key.key, envelope);
|
|
19226
|
-
if (content.type !== "command"
|
|
19152
|
+
if (content.type !== "command" || content.projectId !== envelope.project || content.requestId !== envelope.id)
|
|
19227
19153
|
return;
|
|
19228
|
-
|
|
19229
|
-
if (content.type === "command") Command.parse(content);
|
|
19154
|
+
Command.parse(content);
|
|
19230
19155
|
key.received = envelope.sequence;
|
|
19231
|
-
|
|
19232
|
-
this.config.unconfirmed[content.requestId] = {
|
|
19233
|
-
pairing: pair.id,
|
|
19234
|
-
project: content.projectId
|
|
19235
|
-
};
|
|
19156
|
+
this.config.unconfirmed[content.requestId] = { pairing: pair.id, project: content.projectId };
|
|
19236
19157
|
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
19158
|
await this.execute(pair, content, envelope);
|
|
19246
19159
|
}
|
|
19247
19160
|
async execute(pair, content, envelope) {
|
|
19248
19161
|
let queue = void 0;
|
|
19249
19162
|
try {
|
|
19250
|
-
if (["
|
|
19251
|
-
content.action.kind
|
|
19252
|
-
) && Math.abs(Date.now() - Date.parse(envelope.sentAt)) > EPHEMERAL_TTL_MS)
|
|
19163
|
+
if (["approval.respond", "thread.stop", "session.start"].includes(content.action.kind) && Math.abs(Date.now() - Date.parse(envelope.sentAt)) > EPHEMERAL_TTL_MS)
|
|
19253
19164
|
throw new Error("Command expired. Try again while this computer is online.");
|
|
19254
19165
|
const outcome = await this.command(pair, content);
|
|
19255
19166
|
delete this.config.unconfirmed[content.requestId];
|
|
@@ -19402,12 +19313,32 @@ var Connector = class {
|
|
|
19402
19313
|
if (!thread) throw new Error("Thread not found");
|
|
19403
19314
|
const messages5 = this.discovery.read(thread, this.attached(thread)?.messages);
|
|
19404
19315
|
const history = await publishHistory(this.config, pair, project.id, thread, messages5);
|
|
19316
|
+
this.follows.set(`${pair.id}:${thread.id}`, {
|
|
19317
|
+
pair,
|
|
19318
|
+
project: project.id,
|
|
19319
|
+
thread: thread.id,
|
|
19320
|
+
until: Date.now() + FOLLOW_MS,
|
|
19321
|
+
copy: copy(messages5),
|
|
19322
|
+
updatedAt: thread.updatedAt
|
|
19323
|
+
});
|
|
19405
19324
|
return this.thread(pair, project, thread, history);
|
|
19406
19325
|
}
|
|
19326
|
+
case "attachment.read": {
|
|
19327
|
+
if (!thread) throw new Error("Thread not found");
|
|
19328
|
+
const messages5 = this.discovery.read(thread, this.attached(thread)?.messages);
|
|
19329
|
+
return shareAttachment(
|
|
19330
|
+
this.config,
|
|
19331
|
+
pair,
|
|
19332
|
+
project.id,
|
|
19333
|
+
thread.id,
|
|
19334
|
+
messages5,
|
|
19335
|
+
command2.action.id
|
|
19336
|
+
);
|
|
19337
|
+
}
|
|
19407
19338
|
case "thread.send": {
|
|
19408
19339
|
if (!thread || !thread.capabilities.send)
|
|
19409
19340
|
throw new Error(
|
|
19410
|
-
"This session has no live reply connection.
|
|
19341
|
+
"This session has no live reply connection. Use the existing agent on your computer."
|
|
19411
19342
|
);
|
|
19412
19343
|
const location2 = this.discovery.locations.get(thread.id);
|
|
19413
19344
|
if (location2?.live?.send) return location2.live.send(command2.requestId, command2.action.text);
|
|
@@ -19428,7 +19359,7 @@ var Connector = class {
|
|
|
19428
19359
|
const agent = thread && this.attached(thread);
|
|
19429
19360
|
if (!agent || agent.socket.destroyed)
|
|
19430
19361
|
throw new Error(
|
|
19431
|
-
"This session has no live connection that can stop it.
|
|
19362
|
+
"This session has no live connection that can stop it. Stop the agent on your computer."
|
|
19432
19363
|
);
|
|
19433
19364
|
return commandAcp(agent, command2.requestId, { kind: "cancel" });
|
|
19434
19365
|
}
|
|
@@ -19462,7 +19393,8 @@ var Connector = class {
|
|
|
19462
19393
|
return native.approve(command2.requestId, approval4.id, action.choice);
|
|
19463
19394
|
}
|
|
19464
19395
|
const agent = this.attached(thread);
|
|
19465
|
-
if (!agent)
|
|
19396
|
+
if (!agent)
|
|
19397
|
+
throw new Error("Approval requires an attached ACP session. Answer it on your computer.");
|
|
19466
19398
|
const [request2, approval3] = [...agent.approvals].find(([, item]) => item.id === action.approvalId) ?? [];
|
|
19467
19399
|
if (!approval3?.choices.some((choice) => choice.id === action.choice))
|
|
19468
19400
|
throw new Error("Approval is no longer available");
|
|
@@ -19472,12 +19404,6 @@ var Connector = class {
|
|
|
19472
19404
|
choice: action.choice
|
|
19473
19405
|
});
|
|
19474
19406
|
}
|
|
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
19407
|
}
|
|
19482
19408
|
}
|
|
19483
19409
|
// Starts an agent in a project, or in a new folder inside one this computer offers. Commands run
|
|
@@ -19518,24 +19444,24 @@ var Connector = class {
|
|
|
19518
19444
|
};
|
|
19519
19445
|
|
|
19520
19446
|
// src/install.ts
|
|
19521
|
-
import { existsSync as
|
|
19447
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
19522
19448
|
import { homedir as homedir6, userInfo } from "node:os";
|
|
19523
|
-
import { promisify as
|
|
19524
|
-
import { fileURLToPath as
|
|
19525
|
-
import { execFile as
|
|
19449
|
+
import { promisify as promisify6 } from "node:util";
|
|
19450
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19451
|
+
import { execFile as execFile6, spawn as spawn5 } from "node:child_process";
|
|
19526
19452
|
import { cp, mkdir as mkdir2, rm, writeFile } from "node:fs/promises";
|
|
19527
19453
|
import { dirname as dirname6, join as join10, relative, sep } from "node:path";
|
|
19528
|
-
var
|
|
19454
|
+
var run3 = promisify6(execFile6);
|
|
19529
19455
|
var xml = (value2) => value2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
19530
19456
|
var NODE_FLAGS = ["--disable-warning=ExperimentalWarning"];
|
|
19531
|
-
var systemctl = (...args2) =>
|
|
19457
|
+
var systemctl = (...args2) => run3("systemctl", ["--user", ...args2], {
|
|
19532
19458
|
env: { ...process.env, XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR ?? `/run/user/${userInfo().uid}` }
|
|
19533
19459
|
});
|
|
19534
19460
|
var serviceFile = () => process.platform === "darwin" ? join10(homedir6(), "Library/LaunchAgents/com.dashup.connector.plist") : process.platform === "win32" ? join10(
|
|
19535
19461
|
process.env.APPDATA ?? join10(homedir6(), "AppData/Roaming"),
|
|
19536
19462
|
"Microsoft/Windows/Start Menu/Programs/Startup/Dashup.vbs"
|
|
19537
19463
|
) : join10(homedir6(), ".config/systemd/user/dashup.service");
|
|
19538
|
-
var startupInstalled = () =>
|
|
19464
|
+
var startupInstalled = () => existsSync5(serviceFile());
|
|
19539
19465
|
function service(platform, node, entry, config2) {
|
|
19540
19466
|
const command2 = [node, ...NODE_FLAGS, entry];
|
|
19541
19467
|
if (platform === "darwin")
|
|
@@ -19563,7 +19489,7 @@ WantedBy=default.target
|
|
|
19563
19489
|
`;
|
|
19564
19490
|
}
|
|
19565
19491
|
async function durableEntry() {
|
|
19566
|
-
const entry =
|
|
19492
|
+
const entry = fileURLToPath2(import.meta.url);
|
|
19567
19493
|
if (!entry.endsWith(`${sep}dist${sep}index.js`))
|
|
19568
19494
|
throw new Error(
|
|
19569
19495
|
"Build the connector before installing startup: pnpm --filter @dashup/connector build"
|
|
@@ -19586,13 +19512,13 @@ async function installStartup() {
|
|
|
19586
19512
|
service(process.platform, process.execPath, await durableEntry(), configPath())
|
|
19587
19513
|
);
|
|
19588
19514
|
if (process.platform === "darwin") {
|
|
19589
|
-
await
|
|
19515
|
+
await run3("launchctl", ["unload", path]).catch(() => {
|
|
19590
19516
|
});
|
|
19591
|
-
await
|
|
19517
|
+
await run3("launchctl", ["load", path]);
|
|
19592
19518
|
} else if (process.platform === "win32") {
|
|
19593
|
-
|
|
19519
|
+
spawn5("wscript.exe", [path], { detached: true, stdio: "ignore" }).unref();
|
|
19594
19520
|
} else {
|
|
19595
|
-
const note = process.env.DISPLAY || process.env.WAYLAND_DISPLAY ? void 0 : await
|
|
19521
|
+
const note = process.env.DISPLAY || process.env.WAYLAND_DISPLAY ? void 0 : await run3("loginctl", ["enable-linger"]).then(
|
|
19596
19522
|
() => void 0,
|
|
19597
19523
|
() => `It stops when you log out. To keep it running, run sudo loginctl enable-linger ${userInfo().username}.`
|
|
19598
19524
|
);
|
|
@@ -19604,7 +19530,7 @@ async function installStartup() {
|
|
|
19604
19530
|
}
|
|
19605
19531
|
async function uninstallStartup() {
|
|
19606
19532
|
const path = serviceFile();
|
|
19607
|
-
if (process.platform === "darwin") await
|
|
19533
|
+
if (process.platform === "darwin") await run3("launchctl", ["unload", path]).catch(() => {
|
|
19608
19534
|
});
|
|
19609
19535
|
else if (process.platform !== "win32")
|
|
19610
19536
|
await systemctl("disable", "dashup.service").catch(() => {
|
|
@@ -19709,22 +19635,6 @@ async function main() {
|
|
|
19709
19635
|
if (!response.ok) throw new Error("Could not stop Dashup");
|
|
19710
19636
|
return;
|
|
19711
19637
|
}
|
|
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
19638
|
if (command === "acp") {
|
|
19729
19639
|
const native = args[1] === "--" ? args.slice(2) : args.slice(1);
|
|
19730
19640
|
const [executable, ...agentArgs] = native;
|
|
@@ -19733,18 +19643,7 @@ async function main() {
|
|
|
19733
19643
|
return proxyAcp(config, executable, agentArgs);
|
|
19734
19644
|
}
|
|
19735
19645
|
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
|
-
);
|
|
19646
|
+
const discovery = new Discovery(config.device?.id ?? "local", config.localSecret);
|
|
19748
19647
|
discovery.scan();
|
|
19749
19648
|
const live = new LiveHarnesses();
|
|
19750
19649
|
try {
|
|
@@ -19759,7 +19658,6 @@ async function main() {
|
|
|
19759
19658
|
node: process.version,
|
|
19760
19659
|
paired: Boolean(config.secret),
|
|
19761
19660
|
platform: process.platform,
|
|
19762
|
-
screen: support,
|
|
19763
19661
|
projectCount: projects.length,
|
|
19764
19662
|
harnesses: Object.fromEntries(
|
|
19765
19663
|
Object.keys(HARNESSES).filter((harness) => harness !== "other").map((harness) => [
|
|
@@ -19793,7 +19691,7 @@ async function main() {
|
|
|
19793
19691
|
}
|
|
19794
19692
|
if (!["pair", "watch"].includes(command)) {
|
|
19795
19693
|
process.stdout.write(
|
|
19796
|
-
"Dashup: pair [--account <email>] | watch | projects | alert --project <id> | doctor |
|
|
19694
|
+
"Dashup: pair [--account <email>] | watch | projects | alert --project <id> | doctor | stop | acp -- <original-agent-command> | install | uninstall\n"
|
|
19797
19695
|
);
|
|
19798
19696
|
return;
|
|
19799
19697
|
}
|
|
@@ -19866,7 +19764,7 @@ async function main() {
|
|
|
19866
19764
|
response.end(JSON.stringify(connector.invite()));
|
|
19867
19765
|
return;
|
|
19868
19766
|
}
|
|
19869
|
-
if (
|
|
19767
|
+
if (request2.url !== "/attention" || request2.method !== "POST") {
|
|
19870
19768
|
response.writeHead(404).end();
|
|
19871
19769
|
return;
|
|
19872
19770
|
}
|
|
@@ -19875,12 +19773,6 @@ async function main() {
|
|
|
19875
19773
|
value2 += String(chunk);
|
|
19876
19774
|
if (value2.length > 2e4) throw new Error("Alert too large");
|
|
19877
19775
|
}
|
|
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
19776
|
const input = Attention.parse(JSON.parse(value2));
|
|
19885
19777
|
await connector.attention(
|
|
19886
19778
|
input.projectId,
|