@dashup/connector 0.1.11 → 0.1.13
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 +2 -2
- package/claude/.claude-plugin/marketplace.json +5 -0
- package/claude/.claude-plugin/plugin.json +4 -0
- package/claude/hooks/hooks.json +1 -0
- package/claude/hooks/register.ts +70 -0
- package/dist/index.js +168 -125
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Dashup discovers existing local OpenCode listeners, Codex control sockets, and V
|
|
|
37
37
|
- **OpenCode:** the existing TUI/server supplies conversations, live events, prompts, native tool permissions, and supported single-choice questions. A reply targets the original session ID and preserves its latest native agent, model, and variant. For an existing session, Dashup does not run `opencode serve` or create another one.
|
|
38
38
|
- **Codex:** the existing control socket supplies already-loaded threads. Replies require a recognized native server and a thread that is still loaded and idle immediately before sending. For an existing thread, Dashup never calls `thread/start`, `thread/resume`, or `session/load`. Approval controls appear only for native requests routed to this connection; approvals owned by another client may remain available only in the original UI.
|
|
39
39
|
- **VS Code:** Microsoft's official AHP client subscribes to existing host sessions and chats. Native choices are retained, and delivery is acknowledged by the host. Older extension-host chats without an accessible Agent Host remain saved history. AHP chat IDs are namespaced rather than guessed to equal CLI thread IDs. Non-file remote AHP workspace URIs are not presented as local execution environments.
|
|
40
|
-
- **Claude Code:** existing transcripts are readable without starting Claude.
|
|
40
|
+
- **Claude Code:** existing transcripts are readable without starting Claude. `dashup install` adds Dashup's plugin to Claude Code (`dashup@dashup`, read in place from this package's `claude/` folder), and `dashup uninstall` removes it. A session that starts with the plugin holds a stream open to `~/.dashup/claude.sock` through `curl`, and takes each reply as the person's own words; a reply that starts with `/` runs that command. Claude Code's inbox socket is not used, because Claude presents anything posted there as another session's message, without the person's authority. Sessions opened before the plugin was installed, and sessions on Windows, stay read-only.
|
|
41
41
|
|
|
42
42
|
Native listener discovery supports the host's available process/socket interfaces. File and socket formats are versioned implementation boundaries; unavailable or unfamiliar interfaces leave history readable and controls disabled. Existing editor and agent processes continue independently when the Dashup connector disconnects. The PC's saved SSH relationship is retained; reading it does not grant access to a remote machine's CLI store.
|
|
43
43
|
|
|
@@ -47,7 +47,7 @@ Only an explicit Queue reply starts a temporary official app-server helper to in
|
|
|
47
47
|
|
|
48
48
|
Delivery requires that exact typed client-message ID in the original thread's native history database. A response receipt requires a later native assistant item in the same turn; it does not mean the agent finished. Receipts remain encrypted through the ordinary Dashup relay. History is read on request; the phone asks again while a thread is open and its session changes. Native approvals owned by the private-stdio process remain unsupported and require its original UI. Queue text never substitutes for a native approval.
|
|
49
49
|
|
|
50
|
-
Stop interrupts the running turn through each live interface: OpenCode's session abort, Codex `turn/interrupt`, the AHP client's turn cancellation,
|
|
50
|
+
Stop interrupts the running turn through each live interface: OpenCode's session abort, Codex `turn/interrupt`, the AHP client's turn cancellation, ACP `session/cancel`, and the Claude Code plugin's turn abort. It refuses when nothing is running and expires with other commands after 60 seconds, so a late stop cannot end a newer turn.
|
|
51
51
|
|
|
52
52
|
Live discovery is bounded to 100 sessions per endpoint. OpenCode supplies the latest 200 messages; the VS Code host supplies its current chat snapshot. Paged history prefers the native saved store when available and otherwise uses the existing live snapshot. Approvals whose actual commands or file contents cannot be previewed are approved on the computer; rejection remains available when the native protocol supports it.
|
|
53
53
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "hooks": {}, "modules": ["./register.ts"] }
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { EngineInterface, Register } from 'claude-code';
|
|
2
|
+
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Replies from the Dashup app
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
// Claude Code reads what other processes post to a session's inbox as another session's request,
|
|
8
|
+
// never the person's. Dashup's connector instead streams what the person sends this session from
|
|
9
|
+
// their phone, one delivery per line, and the session takes each message as their own. `now`
|
|
10
|
+
// first ends the running turn, so a delivery without text only stops it. The empty `hooks` in
|
|
11
|
+
// hooks.json lets Claude Code from before function hooks load the plugin, which then does nothing.
|
|
12
|
+
type Delivery = { text?: string; now?: true };
|
|
13
|
+
|
|
14
|
+
const RETRY_MS = 10_000;
|
|
15
|
+
|
|
16
|
+
export const register: Register = (on) => {
|
|
17
|
+
let turnId: string | undefined;
|
|
18
|
+
let stream: AsyncGenerator<unknown, unknown> | undefined;
|
|
19
|
+
on('turn.start', ($, e, next) => {
|
|
20
|
+
if (e.agentId === undefined) turnId = e.turnId;
|
|
21
|
+
return next(e);
|
|
22
|
+
});
|
|
23
|
+
// `/clear` starts another conversation in this process, which streams under its own id.
|
|
24
|
+
on('session.end', ($, e, next) => {
|
|
25
|
+
void stream?.return(undefined);
|
|
26
|
+
return next(e);
|
|
27
|
+
});
|
|
28
|
+
on('session.start', async ($, e, next) => {
|
|
29
|
+
const started = await next(e);
|
|
30
|
+
void (async () => {
|
|
31
|
+
const socketPath = `${await $.env.get('HOME')}/.dashup/claude.sock`;
|
|
32
|
+
for (;;) {
|
|
33
|
+
// A request held open here would hold the session's queue; a child's stream does not.
|
|
34
|
+
const lines = (stream = $.process.spawn({
|
|
35
|
+
argv: [
|
|
36
|
+
'curl',
|
|
37
|
+
'-sfN',
|
|
38
|
+
'--unix-socket',
|
|
39
|
+
socketPath,
|
|
40
|
+
`http://dashup/sessions/${await $.session.id()}`,
|
|
41
|
+
],
|
|
42
|
+
}));
|
|
43
|
+
let buffer = '';
|
|
44
|
+
try {
|
|
45
|
+
for await (const { stream: name, text } of lines) {
|
|
46
|
+
if (name !== 'stdout') continue;
|
|
47
|
+
buffer += text;
|
|
48
|
+
for (let end = buffer.indexOf('\n'); end !== -1; end = buffer.indexOf('\n')) {
|
|
49
|
+
await take($, JSON.parse(buffer.slice(0, end)) as Delivery, turnId);
|
|
50
|
+
buffer = buffer.slice(end + 1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// curl is missing, or the stream broke off mid-line.
|
|
55
|
+
}
|
|
56
|
+
// Dashup is not running on this computer, or is restarting.
|
|
57
|
+
await $.clock.sleep(RETRY_MS);
|
|
58
|
+
}
|
|
59
|
+
})();
|
|
60
|
+
return started;
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// A message runs once the session is idle; the stream must not wait for that.
|
|
65
|
+
async function take($: EngineInterface, { text, now }: Delivery, turnId?: string): Promise<void> {
|
|
66
|
+
if (now && turnId) await $.turn.abort({ turnId }).catch(() => {});
|
|
67
|
+
const command = text?.match(/^\/(\S+)\s*([\s\S]*)$/);
|
|
68
|
+
if (command) void $.command.run({ command: command[1]!, args: command[2] }).catch(() => {});
|
|
69
|
+
else if (text) void $.prompt.submit({ text, asUser: true }).catch(() => {});
|
|
70
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -2149,15 +2149,15 @@ var require_short_unique_id = __commonJS({
|
|
|
2149
2149
|
this.lowerBound = this.dictRange[0];
|
|
2150
2150
|
this.upperBound = this.dictRange[1];
|
|
2151
2151
|
const isAscending = this.lowerBound <= this.upperBound;
|
|
2152
|
-
const
|
|
2152
|
+
const start2 = this.lowerBound;
|
|
2153
2153
|
const end = this.upperBound;
|
|
2154
2154
|
if (isAscending) {
|
|
2155
|
-
for (let i =
|
|
2155
|
+
for (let i = start2; i < end; i++) {
|
|
2156
2156
|
finalDict[dictIdx++] = String.fromCharCode(i);
|
|
2157
2157
|
this.dictIndex = i;
|
|
2158
2158
|
}
|
|
2159
2159
|
} else {
|
|
2160
|
-
for (let i =
|
|
2160
|
+
for (let i = start2; i > end; i--) {
|
|
2161
2161
|
finalDict[dictIdx++] = String.fromCharCode(i);
|
|
2162
2162
|
this.dictIndex = i;
|
|
2163
2163
|
}
|
|
@@ -2706,7 +2706,7 @@ var require_pluralize = __commonJS({
|
|
|
2706
2706
|
|
|
2707
2707
|
// src/index.ts
|
|
2708
2708
|
import { randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
|
|
2709
|
-
import { createServer as
|
|
2709
|
+
import { createServer as createServer3 } from "node:http";
|
|
2710
2710
|
import * as ui2 from "@clack/prompts";
|
|
2711
2711
|
import { z as z3 } from "zod";
|
|
2712
2712
|
|
|
@@ -2742,9 +2742,11 @@ var MESSAGE_ROLES = ["user", "assistant", "system", "tool"];
|
|
|
2742
2742
|
var HARNESSES = {
|
|
2743
2743
|
claude: "Claude Code",
|
|
2744
2744
|
codex: "Codex",
|
|
2745
|
+
kimi: "Kimi",
|
|
2745
2746
|
opencode: "OpenCode",
|
|
2746
2747
|
other: "Agent"
|
|
2747
2748
|
};
|
|
2749
|
+
var isStartHarness = (harness) => harness !== "other" && harness !== "kimi";
|
|
2748
2750
|
|
|
2749
2751
|
// ../../node_modules/.pnpm/@noble+ciphers@2.4.0/node_modules/@noble/ciphers/utils.js
|
|
2750
2752
|
function isBytes(a) {
|
|
@@ -3869,16 +3871,16 @@ function attachAcp(socket, onEvent) {
|
|
|
3869
3871
|
async function proxyAcp(config2, executable, args2) {
|
|
3870
3872
|
if (!config2.localSecret) throw new Error("Start dashup watch before using the ACP proxy");
|
|
3871
3873
|
const socket = await new Promise((resolve3, reject) => {
|
|
3872
|
-
const
|
|
3874
|
+
const connect3 = request({
|
|
3873
3875
|
hostname: "127.0.0.1",
|
|
3874
3876
|
port: connectorPort(),
|
|
3875
3877
|
method: "CONNECT",
|
|
3876
3878
|
path: "/acp",
|
|
3877
3879
|
headers: { Authorization: `Bearer ${config2.localSecret}` }
|
|
3878
3880
|
});
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3881
|
+
connect3.on("connect", (response, stream) => response.statusCode === 200 ? resolve3(stream) : reject(new Error("Dashup ACP connection was rejected")));
|
|
3882
|
+
connect3.on("error", reject);
|
|
3883
|
+
connect3.end();
|
|
3882
3884
|
});
|
|
3883
3885
|
const child = spawn(executable, args2, { stdio: ["pipe", "pipe", "inherit"], windowsHide: true });
|
|
3884
3886
|
const local = /* @__PURE__ */ new Set();
|
|
@@ -4046,7 +4048,7 @@ import * as ui from "@clack/prompts";
|
|
|
4046
4048
|
import { z } from "zod";
|
|
4047
4049
|
|
|
4048
4050
|
// package.json
|
|
4049
|
-
var version = "0.1.
|
|
4051
|
+
var version = "0.1.13";
|
|
4050
4052
|
|
|
4051
4053
|
// src/pair.ts
|
|
4052
4054
|
var Invitation = z.object({
|
|
@@ -4200,6 +4202,7 @@ var sessionKey = (session) => `${session.harness}\0${session.id}`;
|
|
|
4200
4202
|
function agentHarness(name) {
|
|
4201
4203
|
if (/claude/i.test(name)) return "claude";
|
|
4202
4204
|
if (/codex/i.test(name)) return "codex";
|
|
4205
|
+
if (/kimi/i.test(name)) return "kimi";
|
|
4203
4206
|
if (/opencode/i.test(name)) return "opencode";
|
|
4204
4207
|
return void 0;
|
|
4205
4208
|
}
|
|
@@ -4240,13 +4243,13 @@ function records(buffer) {
|
|
|
4240
4243
|
function unchanged(left, right) {
|
|
4241
4244
|
return left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs;
|
|
4242
4245
|
}
|
|
4243
|
-
function readRange(path,
|
|
4246
|
+
function readRange(path, start2, length) {
|
|
4244
4247
|
const fd = openSync2(path, "r");
|
|
4245
4248
|
try {
|
|
4246
4249
|
const buffer = Buffer.alloc(length);
|
|
4247
4250
|
let offset = 0;
|
|
4248
4251
|
while (offset < length) {
|
|
4249
|
-
const count = readSync(fd, buffer, offset, length - offset,
|
|
4252
|
+
const count = readSync(fd, buffer, offset, length - offset, start2 + offset);
|
|
4250
4253
|
if (!count) break;
|
|
4251
4254
|
offset += count;
|
|
4252
4255
|
}
|
|
@@ -4274,12 +4277,12 @@ function described(path, until, describe2) {
|
|
|
4274
4277
|
}
|
|
4275
4278
|
let tail = [];
|
|
4276
4279
|
for (let bytes = SAMPLE_BYTES; stamp.size > head.length; bytes *= 2) {
|
|
4277
|
-
const
|
|
4278
|
-
const buffer = readRange(path,
|
|
4280
|
+
const start2 = Math.max(head.length, stamp.size - bytes);
|
|
4281
|
+
const buffer = readRange(path, start2, stamp.size - start2);
|
|
4279
4282
|
tail = records(
|
|
4280
|
-
readRange(path,
|
|
4283
|
+
readRange(path, start2 - 1, 1)[0] === 10 ? buffer : buffer.subarray(buffer.indexOf(10) + 1)
|
|
4281
4284
|
);
|
|
4282
|
-
if (
|
|
4285
|
+
if (start2 === head.length || tail.some(until)) break;
|
|
4283
4286
|
}
|
|
4284
4287
|
const value2 = describe2([...records(head), ...tail], stamp);
|
|
4285
4288
|
metadata.set(path, { stamp, value: value2 });
|
|
@@ -4296,8 +4299,8 @@ function journal(path, latest = false) {
|
|
|
4296
4299
|
return tail2 ? [...saved.rows, tail2] : saved.rows;
|
|
4297
4300
|
}
|
|
4298
4301
|
if (!saved || saved.stamp.ino !== stamp.ino || stamp.size <= saved.offset || latest && stamp.size - saved.start > 2 * LATEST_BYTES) {
|
|
4299
|
-
const
|
|
4300
|
-
saved = { stamp, start, offset:
|
|
4302
|
+
const start2 = latest ? Math.max(0, stamp.size - LATEST_BYTES) : 0;
|
|
4303
|
+
saved = { stamp, start: start2, offset: start2, tail: Buffer.alloc(0), rows: [] };
|
|
4301
4304
|
}
|
|
4302
4305
|
while (saved.offset < stamp.size) {
|
|
4303
4306
|
const chunk = readRange(path, saved.offset, Math.min(READ_BYTES, stamp.size - saved.offset));
|
|
@@ -4419,8 +4422,8 @@ function said(input, summary) {
|
|
|
4419
4422
|
const line = typeof value2 === "string" ? value2.trim().split("\n")[0] : "";
|
|
4420
4423
|
return line && line !== summary ? line.slice(0, HISTORY_TOOL_CHARS) : void 0;
|
|
4421
4424
|
}
|
|
4422
|
-
function elapsed(
|
|
4423
|
-
const ms = instant(end) - instant(
|
|
4425
|
+
function elapsed(start2, end) {
|
|
4426
|
+
const ms = instant(end) - instant(start2);
|
|
4424
4427
|
return Number.isFinite(ms) ? Math.max(0, Math.round(ms)) : void 0;
|
|
4425
4428
|
}
|
|
4426
4429
|
function added(path, content) {
|
|
@@ -5288,7 +5291,7 @@ var Discovery = class {
|
|
|
5288
5291
|
// src/connector.ts
|
|
5289
5292
|
import { z as z2 } from "zod";
|
|
5290
5293
|
import { join as join9 } from "node:path";
|
|
5291
|
-
import { mkdir } from "node:fs/promises";
|
|
5294
|
+
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
5292
5295
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
5293
5296
|
|
|
5294
5297
|
// ../../node_modules/.pnpm/engine.io-client@6.6.7/node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js
|
|
@@ -7433,10 +7436,10 @@ var Decoder = class _Decoder extends Emitter {
|
|
|
7433
7436
|
throw new Error("unknown packet type " + p.type);
|
|
7434
7437
|
}
|
|
7435
7438
|
if (p.type === PacketType.BINARY_EVENT || p.type === PacketType.BINARY_ACK) {
|
|
7436
|
-
const
|
|
7439
|
+
const start2 = i + 1;
|
|
7437
7440
|
while (str.charAt(++i) !== "-" && i != str.length) {
|
|
7438
7441
|
}
|
|
7439
|
-
const buf = str.substring(
|
|
7442
|
+
const buf = str.substring(start2, i);
|
|
7440
7443
|
if (buf != Number(buf) || str.charAt(i) !== "-") {
|
|
7441
7444
|
throw new Error("Illegal attachments");
|
|
7442
7445
|
}
|
|
@@ -7449,7 +7452,7 @@ var Decoder = class _Decoder extends Emitter {
|
|
|
7449
7452
|
p.attachments = n;
|
|
7450
7453
|
}
|
|
7451
7454
|
if ("/" === str.charAt(i + 1)) {
|
|
7452
|
-
const
|
|
7455
|
+
const start2 = i + 1;
|
|
7453
7456
|
while (++i) {
|
|
7454
7457
|
const c = str.charAt(i);
|
|
7455
7458
|
if ("," === c)
|
|
@@ -7457,13 +7460,13 @@ var Decoder = class _Decoder extends Emitter {
|
|
|
7457
7460
|
if (i === str.length)
|
|
7458
7461
|
break;
|
|
7459
7462
|
}
|
|
7460
|
-
p.nsp = str.substring(
|
|
7463
|
+
p.nsp = str.substring(start2, i);
|
|
7461
7464
|
} else {
|
|
7462
7465
|
p.nsp = "/";
|
|
7463
7466
|
}
|
|
7464
7467
|
const next = str.charAt(i + 1);
|
|
7465
7468
|
if ("" !== next && Number(next) == next) {
|
|
7466
|
-
const
|
|
7469
|
+
const start2 = i + 1;
|
|
7467
7470
|
while (++i) {
|
|
7468
7471
|
const c = str.charAt(i);
|
|
7469
7472
|
if (null == c || Number(c) != c) {
|
|
@@ -7473,7 +7476,7 @@ var Decoder = class _Decoder extends Emitter {
|
|
|
7473
7476
|
if (i === str.length)
|
|
7474
7477
|
break;
|
|
7475
7478
|
}
|
|
7476
|
-
p.id = Number(str.substring(
|
|
7479
|
+
p.id = Number(str.substring(start2, i + 1));
|
|
7477
7480
|
}
|
|
7478
7481
|
if (str.charAt(++i)) {
|
|
7479
7482
|
const payload = this.tryParse(str.substr(i));
|
|
@@ -9683,7 +9686,7 @@ var putShortMSB = (s, b) => {
|
|
|
9683
9686
|
s.pending_buf[s.pending++] = b >>> 8 & 255;
|
|
9684
9687
|
s.pending_buf[s.pending++] = b & 255;
|
|
9685
9688
|
};
|
|
9686
|
-
var read_buf = (strm, buf,
|
|
9689
|
+
var read_buf = (strm, buf, start2, size) => {
|
|
9687
9690
|
let len = strm.avail_in;
|
|
9688
9691
|
if (len > size) {
|
|
9689
9692
|
len = size;
|
|
@@ -9692,11 +9695,11 @@ var read_buf = (strm, buf, start, size) => {
|
|
|
9692
9695
|
return 0;
|
|
9693
9696
|
}
|
|
9694
9697
|
strm.avail_in -= len;
|
|
9695
|
-
buf.set(strm.input.subarray(strm.next_in, strm.next_in + len),
|
|
9698
|
+
buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start2);
|
|
9696
9699
|
if (strm.state.wrap === 1) {
|
|
9697
|
-
strm.adler = adler32_1(strm.adler, buf, len,
|
|
9700
|
+
strm.adler = adler32_1(strm.adler, buf, len, start2);
|
|
9698
9701
|
} else if (strm.state.wrap === 2) {
|
|
9699
|
-
strm.adler = crc32_1(strm.adler, buf, len,
|
|
9702
|
+
strm.adler = crc32_1(strm.adler, buf, len, start2);
|
|
9700
9703
|
}
|
|
9701
9704
|
strm.next_in += len;
|
|
9702
9705
|
strm.total_in += len;
|
|
@@ -11061,7 +11064,7 @@ var deflate_1$1 = {
|
|
|
11061
11064
|
};
|
|
11062
11065
|
var BAD$1 = 16209;
|
|
11063
11066
|
var TYPE$1 = 16191;
|
|
11064
|
-
var inffast = function inflate_fast(strm,
|
|
11067
|
+
var inffast = function inflate_fast(strm, start2) {
|
|
11065
11068
|
let _in;
|
|
11066
11069
|
let last;
|
|
11067
11070
|
let _out;
|
|
@@ -11091,7 +11094,7 @@ var inffast = function inflate_fast(strm, start) {
|
|
|
11091
11094
|
last = _in + (strm.avail_in - 5);
|
|
11092
11095
|
_out = strm.next_out;
|
|
11093
11096
|
output = strm.output;
|
|
11094
|
-
beg = _out - (
|
|
11097
|
+
beg = _out - (start2 - strm.avail_out);
|
|
11095
11098
|
end = _out + (strm.avail_out - 257);
|
|
11096
11099
|
dmax = state.dmax;
|
|
11097
11100
|
wsize = state.wsize;
|
|
@@ -16199,14 +16202,14 @@ var preview = (text3) => text3.split("\n", OUTPUT_PREVIEW.lines).join("\n").slic
|
|
|
16199
16202
|
function splitHistoryMessages(messages5, opaqueId2) {
|
|
16200
16203
|
return messages5.flatMap((message) => {
|
|
16201
16204
|
const text3 = [];
|
|
16202
|
-
let
|
|
16205
|
+
let start2 = 0;
|
|
16203
16206
|
do {
|
|
16204
|
-
let end = Math.min(
|
|
16207
|
+
let end = Math.min(start2 + HISTORY_TEXT_CHARS, message.text.length);
|
|
16205
16208
|
const code = message.text.charCodeAt(end - 1);
|
|
16206
16209
|
if (end < message.text.length && code >= 55296 && code <= 56319) end--;
|
|
16207
|
-
text3.push(message.text.slice(
|
|
16208
|
-
|
|
16209
|
-
} while (
|
|
16210
|
+
text3.push(message.text.slice(start2, end));
|
|
16211
|
+
start2 = end;
|
|
16212
|
+
} while (start2 < message.text.length);
|
|
16210
16213
|
const id = opaqueId2(message.id);
|
|
16211
16214
|
return text3.map((part, index) => ({
|
|
16212
16215
|
id,
|
|
@@ -16303,9 +16306,9 @@ async function publishHistory(config2, pair, project, thread, messages5) {
|
|
|
16303
16306
|
const generation = ++local.sequence;
|
|
16304
16307
|
saveConfig(config2);
|
|
16305
16308
|
const all = historyMessages(local.key, project, thread.id, messages5);
|
|
16306
|
-
let
|
|
16307
|
-
while (
|
|
16308
|
-
const fragments =
|
|
16309
|
+
let start2 = Math.max(0, all.length - HISTORY_KEPT);
|
|
16310
|
+
while (start2 < all.length && (all[start2].part ?? 0) > 0) start2++;
|
|
16311
|
+
const fragments = start2 ? [...historyMessages(local.key, project, thread.id, [EARLIER]), ...all.slice(start2)] : all;
|
|
16309
16312
|
const summary = { generation, total: fragments.length, updatedAt: thread.updatedAt };
|
|
16310
16313
|
const batch = {
|
|
16311
16314
|
pairing: pair.id,
|
|
@@ -16359,8 +16362,8 @@ function kept(message) {
|
|
|
16359
16362
|
files2.push(attached(`${message.id}@${at}`, mime, data));
|
|
16360
16363
|
return "";
|
|
16361
16364
|
});
|
|
16362
|
-
const
|
|
16363
|
-
const full =
|
|
16365
|
+
const start2 = message.role === "tool" && !message.tool?.diff ? preview(text3) : text3;
|
|
16366
|
+
const full = start2.length < text3.length ? {
|
|
16364
16367
|
id: `${message.id}:full`,
|
|
16365
16368
|
mime: "text/plain",
|
|
16366
16369
|
bytes: Buffer.byteLength(text3),
|
|
@@ -16368,7 +16371,7 @@ function kept(message) {
|
|
|
16368
16371
|
} : void 0;
|
|
16369
16372
|
return {
|
|
16370
16373
|
...message,
|
|
16371
|
-
text:
|
|
16374
|
+
text: start2,
|
|
16372
16375
|
...full ? { full } : {},
|
|
16373
16376
|
...files2.length ? { attachments: files2 } : {}
|
|
16374
16377
|
};
|
|
@@ -16789,17 +16792,17 @@ var NOTIFY_MS = 100;
|
|
|
16789
16792
|
function hunk(before, after, offset = 0) {
|
|
16790
16793
|
const old = before ? before.replace(/\n$/, "").split("\n") : [];
|
|
16791
16794
|
const next = after ? after.replace(/\n$/, "").split("\n") : [];
|
|
16792
|
-
let
|
|
16793
|
-
while (
|
|
16795
|
+
let start2 = 0;
|
|
16796
|
+
while (start2 < old.length && start2 < next.length && old[start2] === next[start2]) start2++;
|
|
16794
16797
|
let end = 0;
|
|
16795
|
-
while (end < old.length -
|
|
16798
|
+
while (end < old.length - start2 && end < next.length - start2 && old[old.length - 1 - end] === next[next.length - 1 - end])
|
|
16796
16799
|
end++;
|
|
16797
|
-
const removed = old.slice(
|
|
16798
|
-
const added2 = next.slice(
|
|
16800
|
+
const removed = old.slice(start2, old.length - end);
|
|
16801
|
+
const added2 = next.slice(start2, next.length - end);
|
|
16799
16802
|
return {
|
|
16800
16803
|
lines: [
|
|
16801
|
-
`@@ -${offset +
|
|
16802
|
-
...old.slice(Math.max(0,
|
|
16804
|
+
`@@ -${offset + start2 + 1},${removed.length} +${offset + start2 + 1},${added2.length} @@`,
|
|
16805
|
+
...old.slice(Math.max(0, start2 - 3), start2).map((line) => ` ${line}`),
|
|
16803
16806
|
...removed.map((line) => `-${line}`),
|
|
16804
16807
|
...added2.map((line) => `+${line}`),
|
|
16805
16808
|
...old.slice(old.length - end, old.length - end + 3).map((line) => ` ${line}`)
|
|
@@ -17082,16 +17085,13 @@ var Headless = class {
|
|
|
17082
17085
|
};
|
|
17083
17086
|
|
|
17084
17087
|
// src/harness/claude.ts
|
|
17085
|
-
import { join as join5 } from "node:path";
|
|
17086
|
-
import { connect as connect2 } from "node:net";
|
|
17088
|
+
import { dirname as dirname4, join as join5 } from "node:path";
|
|
17087
17089
|
import { watch as watch2 } from "node:fs";
|
|
17088
|
-
import {
|
|
17089
|
-
|
|
17090
|
-
var PEER_PROTOCOL = 1;
|
|
17090
|
+
import { mkdir, readdir as readdir2, readFile as readFile2, rm } from "node:fs/promises";
|
|
17091
|
+
import { createServer as createServer2 } from "node:http";
|
|
17091
17092
|
var MAX_SESSIONS2 = 100;
|
|
17092
|
-
var REQUEST_MS2 = 4e3;
|
|
17093
17093
|
var MAX_REQUESTS2 = 1e3;
|
|
17094
|
-
var
|
|
17094
|
+
var SESSION = /^\/sessions\/([\w-]{1,100})$/;
|
|
17095
17095
|
var STATUS = {
|
|
17096
17096
|
busy: "working",
|
|
17097
17097
|
idle: "idle",
|
|
@@ -17116,39 +17116,22 @@ async function registrations() {
|
|
|
17116
17116
|
);
|
|
17117
17117
|
return found.flat();
|
|
17118
17118
|
}
|
|
17119
|
-
async function post(session, path, text3, priority) {
|
|
17120
|
-
const file = await lstat(path);
|
|
17121
|
-
if (!file.isSocket() || process.getuid && file.uid !== process.getuid())
|
|
17122
|
-
throw new Error("Claude Code inbox is not owned by this user");
|
|
17123
|
-
const body2 = text3.replace(new RegExp(`<(?=/?${ENVELOPE})`, "gi"), "<\\");
|
|
17124
|
-
const line = JSON.stringify({
|
|
17125
|
-
type: "user",
|
|
17126
|
-
session_id: session.sessionId,
|
|
17127
|
-
priority,
|
|
17128
|
-
message: { role: "user", content: `<${ENVELOPE} from-name="Dashup">
|
|
17129
|
-
${body2}
|
|
17130
|
-
</${ENVELOPE}>` }
|
|
17131
|
-
});
|
|
17132
|
-
await new Promise((resolve3, reject) => {
|
|
17133
|
-
const socket = connect2(path);
|
|
17134
|
-
socket.setTimeout(REQUEST_MS2, () => socket.destroy(new Error("timed out")));
|
|
17135
|
-
socket.once("error", () => reject(new Error("Claude Code is not accepting messages")));
|
|
17136
|
-
socket.end(`${line}
|
|
17137
|
-
`, resolve3);
|
|
17138
|
-
});
|
|
17139
|
-
}
|
|
17140
17119
|
var Claude = class {
|
|
17141
|
-
constructor(changed) {
|
|
17120
|
+
constructor(changed, socket = join5(dirname4(configPath()), "claude.sock")) {
|
|
17142
17121
|
this.changed = changed;
|
|
17122
|
+
this.socket = socket;
|
|
17143
17123
|
}
|
|
17144
17124
|
changed;
|
|
17125
|
+
socket;
|
|
17145
17126
|
watcher;
|
|
17146
17127
|
notify;
|
|
17128
|
+
server;
|
|
17129
|
+
streams = /* @__PURE__ */ new Map();
|
|
17147
17130
|
requests = /* @__PURE__ */ new Map();
|
|
17148
17131
|
async refresh() {
|
|
17149
17132
|
this.watch();
|
|
17150
17133
|
return (await registrations()).map((session) => {
|
|
17151
|
-
const
|
|
17134
|
+
const plugin = this.streams.has(session.sessionId);
|
|
17152
17135
|
return {
|
|
17153
17136
|
id: session.sessionId,
|
|
17154
17137
|
harness: "claude",
|
|
@@ -17157,16 +17140,47 @@ var Claude = class {
|
|
|
17157
17140
|
status: STATUS[session.status ?? ""] ?? "unknown",
|
|
17158
17141
|
messages: [],
|
|
17159
17142
|
approvals: [],
|
|
17160
|
-
send:
|
|
17161
|
-
stop:
|
|
17162
|
-
now:
|
|
17143
|
+
send: plugin ? (requestId, text3, now) => this.once(requestId, () => this.deliver(session.sessionId, now ? { text: text3, now } : { text: text3 })) : void 0,
|
|
17144
|
+
stop: plugin ? (requestId) => this.once(requestId, () => this.deliver(session.sessionId, { now: true })) : void 0,
|
|
17145
|
+
now: plugin
|
|
17163
17146
|
};
|
|
17164
17147
|
});
|
|
17165
17148
|
}
|
|
17149
|
+
// Only the connector serves the plugin; one-off commands such as `dashup doctor` read alone.
|
|
17150
|
+
async listen() {
|
|
17151
|
+
if (this.server || process.env.DASHUP_LIVE_DISCOVERY === "0") return;
|
|
17152
|
+
const server = this.server = createServer2((request2, response) => {
|
|
17153
|
+
const id = request2.method === "GET" ? SESSION.exec(request2.url ?? "")?.[1] : void 0;
|
|
17154
|
+
if (!id) return void response.writeHead(404).end();
|
|
17155
|
+
this.streams.get(id)?.end();
|
|
17156
|
+
this.streams.set(id, response);
|
|
17157
|
+
response.once("close", () => {
|
|
17158
|
+
if (this.streams.get(id) === response) this.streams.delete(id);
|
|
17159
|
+
this.changed();
|
|
17160
|
+
});
|
|
17161
|
+
response.writeHead(200, { "Content-Type": "application/x-ndjson" }).flushHeaders();
|
|
17162
|
+
this.changed();
|
|
17163
|
+
});
|
|
17164
|
+
await mkdir(dirname4(this.socket), { recursive: true, mode: 448 });
|
|
17165
|
+
await rm(this.socket, { force: true });
|
|
17166
|
+
if (this.server !== server) return;
|
|
17167
|
+
await new Promise((resolve3, reject) => {
|
|
17168
|
+
server.once("error", reject);
|
|
17169
|
+
server.listen(this.socket, resolve3);
|
|
17170
|
+
});
|
|
17171
|
+
}
|
|
17172
|
+
deliver(id, delivery) {
|
|
17173
|
+
const stream = this.streams.get(id);
|
|
17174
|
+
if (!stream) return Promise.reject(new Error("Claude Code is not taking messages"));
|
|
17175
|
+
return new Promise((resolve3, reject) => stream.write(`${JSON.stringify(delivery)}
|
|
17176
|
+
`, (error) => error ? reject(error) : resolve3()));
|
|
17177
|
+
}
|
|
17178
|
+
// Each request delivers at most once.
|
|
17166
17179
|
once(id, action) {
|
|
17167
17180
|
const known = this.requests.get(id);
|
|
17168
17181
|
if (known) return known;
|
|
17169
|
-
if (this.requests.size >= MAX_REQUESTS2)
|
|
17182
|
+
if (this.requests.size >= MAX_REQUESTS2)
|
|
17183
|
+
this.requests.delete(this.requests.keys().next().value);
|
|
17170
17184
|
const result = action();
|
|
17171
17185
|
this.requests.set(id, result);
|
|
17172
17186
|
return result;
|
|
@@ -17193,23 +17207,26 @@ var Claude = class {
|
|
|
17193
17207
|
clearTimeout(this.notify);
|
|
17194
17208
|
this.watcher?.close();
|
|
17195
17209
|
this.watcher = void 0;
|
|
17210
|
+
this.server?.closeAllConnections();
|
|
17211
|
+
this.server?.close();
|
|
17212
|
+
this.server = void 0;
|
|
17196
17213
|
}
|
|
17197
17214
|
};
|
|
17198
17215
|
|
|
17199
17216
|
// src/harness/vscode.ts
|
|
17200
17217
|
import Socket3 from "ws";
|
|
17201
17218
|
import { homedir as homedir5 } from "node:os";
|
|
17202
|
-
import { connect as
|
|
17219
|
+
import { connect as connect2 } from "node:net";
|
|
17203
17220
|
import { promisify as promisify3 } from "node:util";
|
|
17204
17221
|
import { fileURLToPath } from "node:url";
|
|
17205
17222
|
import { execFile as execFile3 } from "node:child_process";
|
|
17206
17223
|
import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
|
|
17207
17224
|
import { stat as stat2, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
17208
|
-
import { join as join6, dirname as
|
|
17225
|
+
import { join as join6, dirname as dirname5, basename as basename2, isAbsolute as isAbsolute3 } from "node:path";
|
|
17209
17226
|
import { WebSocketTransport } from "@microsoft/agent-host-protocol/ws";
|
|
17210
17227
|
import { AhpClient } from "@microsoft/agent-host-protocol/client";
|
|
17211
17228
|
import { chatReducer, sessionReducer, SUPPORTED_PROTOCOL_VERSIONS } from "@microsoft/agent-host-protocol";
|
|
17212
|
-
var
|
|
17229
|
+
var REQUEST_MS2 = 4e3;
|
|
17213
17230
|
var MAX_BYTES2 = 8 * 1024 * 1024;
|
|
17214
17231
|
var MAX_METADATA = 64 * 1024;
|
|
17215
17232
|
var MAX_HOSTS = 16;
|
|
@@ -17223,7 +17240,7 @@ var CONFIRMATION_CHOICES = [
|
|
|
17223
17240
|
function roots() {
|
|
17224
17241
|
const override = process.env.DASHUP_AHP_DATA ?? process.env.DASHUP_VSCODE_DATA;
|
|
17225
17242
|
if (override !== void 0)
|
|
17226
|
-
return override ? [basename2(override) === "User" ?
|
|
17243
|
+
return override ? [basename2(override) === "User" ? dirname5(override) : override] : [];
|
|
17227
17244
|
if (process.env.DASHUP_LIVE_DISCOVERY === "0") return [];
|
|
17228
17245
|
const base = process.platform === "darwin" ? join6(homedir5(), "Library/Application Support") : process.platform === "win32" ? process.env.APPDATA ?? join6(homedir5(), "AppData/Roaming") : process.env.XDG_CONFIG_HOME ?? join6(homedir5(), ".config");
|
|
17229
17246
|
return ["Code", "Code - Insiders"].map((name) => join6(base, name));
|
|
@@ -17243,7 +17260,7 @@ async function privateFile(path, limit) {
|
|
|
17243
17260
|
"-EncodedCommand",
|
|
17244
17261
|
Buffer.from(script, "utf16le").toString("base64")
|
|
17245
17262
|
],
|
|
17246
|
-
{ timeout:
|
|
17263
|
+
{ timeout: REQUEST_MS2, maxBuffer: MAX_METADATA }
|
|
17247
17264
|
);
|
|
17248
17265
|
return stdout.trim().toLowerCase() === "true";
|
|
17249
17266
|
} catch {
|
|
@@ -17284,7 +17301,7 @@ async function entries() {
|
|
|
17284
17301
|
if (!socket?.isSocket() || process.getuid && socket.uid !== process.getuid())
|
|
17285
17302
|
continue;
|
|
17286
17303
|
}
|
|
17287
|
-
if (!await privateFile(
|
|
17304
|
+
if (!await privateFile(dirname5(path), MAX_BYTES2)) continue;
|
|
17288
17305
|
const key = `${root}\0${identity2}`;
|
|
17289
17306
|
if (!result.has(key))
|
|
17290
17307
|
result.set(key, {
|
|
@@ -17448,9 +17465,9 @@ var Host = class {
|
|
|
17448
17465
|
if (this.connecting) return this.connecting;
|
|
17449
17466
|
this.connecting = (async () => {
|
|
17450
17467
|
const socket = new Socket3(`ws://localhost/?tkn=${encodeURIComponent(this.entry.token)}`, {
|
|
17451
|
-
createConnection: () =>
|
|
17468
|
+
createConnection: () => connect2(this.entry.path),
|
|
17452
17469
|
maxPayload: MAX_BYTES2,
|
|
17453
|
-
handshakeTimeout:
|
|
17470
|
+
handshakeTimeout: REQUEST_MS2
|
|
17454
17471
|
});
|
|
17455
17472
|
socket.on("error", () => {
|
|
17456
17473
|
});
|
|
@@ -17483,7 +17500,7 @@ var Host = class {
|
|
|
17483
17500
|
}
|
|
17484
17501
|
};
|
|
17485
17502
|
const client = this.client = new AhpClient(transport, {
|
|
17486
|
-
requestTimeoutMs:
|
|
17503
|
+
requestTimeoutMs: REQUEST_MS2,
|
|
17487
17504
|
subscriptionBuffer: 1024
|
|
17488
17505
|
});
|
|
17489
17506
|
this.resources.clear();
|
|
@@ -17547,7 +17564,7 @@ var Host = class {
|
|
|
17547
17564
|
const timer = setTimeout(() => {
|
|
17548
17565
|
this.receipts.delete(handle.clientSeq);
|
|
17549
17566
|
reject(new Error("Native AHP action was not acknowledged; delivery is unknown"));
|
|
17550
|
-
},
|
|
17567
|
+
}, REQUEST_MS2);
|
|
17551
17568
|
this.receipts.set(handle.clientSeq, { resolve: resolve3, reject, timer });
|
|
17552
17569
|
});
|
|
17553
17570
|
this.requests.set(requestId, pending);
|
|
@@ -17701,7 +17718,7 @@ import { createHash as createHash4 } from "node:crypto";
|
|
|
17701
17718
|
import { execFile as execFile4, spawn as spawn3 } from "node:child_process";
|
|
17702
17719
|
import { stat as stat3, readlink, readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
|
|
17703
17720
|
var run2 = promisify4(execFile4);
|
|
17704
|
-
var
|
|
17721
|
+
var REQUEST_MS3 = 4e3;
|
|
17705
17722
|
var MAX_BYTES3 = 8 * 1024 * 1024;
|
|
17706
17723
|
var MAX_SESSIONS4 = 100;
|
|
17707
17724
|
var MAX_PROCESSES2 = 4096;
|
|
@@ -17783,7 +17800,7 @@ async function endpoints() {
|
|
|
17783
17800
|
const { stdout } = await run2(
|
|
17784
17801
|
"lsof",
|
|
17785
17802
|
["-nP", "-a", "-c", "opencode", "-iTCP", "-sTCP:LISTEN", "-Fn"],
|
|
17786
|
-
{ timeout:
|
|
17803
|
+
{ timeout: REQUEST_MS3, maxBuffer: MAX_BYTES3 }
|
|
17787
17804
|
);
|
|
17788
17805
|
return stdout.split("\n").flatMap((line) => {
|
|
17789
17806
|
const port = /^n(?:127\.0\.0\.1|\[::1\]|\*):(\d+)$/.exec(line)?.[1];
|
|
@@ -17800,7 +17817,7 @@ async function endpoints() {
|
|
|
17800
17817
|
const { stdout } = await run2(
|
|
17801
17818
|
"powershell.exe",
|
|
17802
17819
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
17803
|
-
{ timeout:
|
|
17820
|
+
{ timeout: REQUEST_MS3, maxBuffer: MAX_BYTES3 }
|
|
17804
17821
|
);
|
|
17805
17822
|
return stdout.split(/\r?\n/).filter((port) => /^\d+$/.test(port)).slice(0, MAX_ENDPOINTS).map((port) => ({
|
|
17806
17823
|
url: localEndpoint(`http://127.0.0.1:${port}`),
|
|
@@ -17920,7 +17937,7 @@ var Server = class {
|
|
|
17920
17937
|
};
|
|
17921
17938
|
}
|
|
17922
17939
|
async request(path, directory2, payload) {
|
|
17923
|
-
const signal = AbortSignal.timeout(
|
|
17940
|
+
const signal = AbortSignal.timeout(REQUEST_MS3);
|
|
17924
17941
|
const response = await fetch(this.endpoint.url + path, {
|
|
17925
17942
|
method: payload === void 0 ? "GET" : "POST",
|
|
17926
17943
|
headers: {
|
|
@@ -18096,7 +18113,7 @@ var Server = class {
|
|
|
18096
18113
|
listen() {
|
|
18097
18114
|
if (this.stream || this.retry || this.stopped) return;
|
|
18098
18115
|
const controller = this.stream = new AbortController();
|
|
18099
|
-
const connectTimer = setTimeout(() => controller.abort(),
|
|
18116
|
+
const connectTimer = setTimeout(() => controller.abort(), REQUEST_MS3);
|
|
18100
18117
|
connectTimer.unref();
|
|
18101
18118
|
void (async () => {
|
|
18102
18119
|
const response = await fetch(this.endpoint.url + "/global/event", {
|
|
@@ -18318,6 +18335,10 @@ var LiveHarnesses = class {
|
|
|
18318
18335
|
}
|
|
18319
18336
|
watch(onChange) {
|
|
18320
18337
|
this.listeners.add(onChange);
|
|
18338
|
+
this.claude.listen().catch((error) => process.stderr.write(
|
|
18339
|
+
`Claude Code sessions cannot take replies: ${error instanceof Error ? error.message : error}
|
|
18340
|
+
`
|
|
18341
|
+
));
|
|
18321
18342
|
if (!this.timer && process.env.DASHUP_LIVE_DISCOVERY !== "0") {
|
|
18322
18343
|
this.timer = setInterval(() => this.changed(), DISCOVERY_MS);
|
|
18323
18344
|
this.timer.unref();
|
|
@@ -18346,10 +18367,10 @@ var LiveHarnesses = class {
|
|
|
18346
18367
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18347
18368
|
import { spawn as spawn4 } from "node:child_process";
|
|
18348
18369
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
18349
|
-
import { join as join8, dirname as
|
|
18370
|
+
import { join as join8, dirname as dirname6, basename as basename3 } from "node:path";
|
|
18350
18371
|
import { statSync as statSync2, existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync4, readlinkSync, realpathSync as realpathSync2 } from "node:fs";
|
|
18351
18372
|
var CODEX_VERSION = "0.160.1";
|
|
18352
|
-
var
|
|
18373
|
+
var REQUEST_MS4 = 1e4;
|
|
18353
18374
|
var MAX_BYTES4 = 1024 * 1024;
|
|
18354
18375
|
var MAX_PROCESSES3 = 4096;
|
|
18355
18376
|
var MAX_FILES = 4096;
|
|
@@ -18365,8 +18386,8 @@ function sameFile(left, right) {
|
|
|
18365
18386
|
return left.path === right.path && left.device === right.device && left.inode === right.inode;
|
|
18366
18387
|
}
|
|
18367
18388
|
function packageVersion(executable) {
|
|
18368
|
-
let directory2 =
|
|
18369
|
-
for (let depth = 0; depth < 8; depth++, directory2 =
|
|
18389
|
+
let directory2 = dirname6(executable);
|
|
18390
|
+
for (let depth = 0; depth < 8; depth++, directory2 = dirname6(directory2)) {
|
|
18370
18391
|
const path = join8(directory2, "package.json");
|
|
18371
18392
|
if (!existsSync4(path)) continue;
|
|
18372
18393
|
const value2 = JSON.parse(readFileSync4(path, "utf8"));
|
|
@@ -18380,8 +18401,8 @@ function processOwners(pid) {
|
|
|
18380
18401
|
const executable = realpathSync2.native(join8(root, "exe"));
|
|
18381
18402
|
if (basename3(executable) !== "codex" || packageVersion(executable) !== CODEX_VERSION) return [];
|
|
18382
18403
|
const status2 = readFileSync4(join8(root, "stat"), "utf8");
|
|
18383
|
-
const
|
|
18384
|
-
if (!
|
|
18404
|
+
const start2 = status2.slice(status2.lastIndexOf(")") + 2).split(" ")[19];
|
|
18405
|
+
if (!start2 || !/^\d+$/.test(start2)) return [];
|
|
18385
18406
|
const fds = readdirSync3(join8(root, "fd")).slice(0, MAX_FILES);
|
|
18386
18407
|
const files2 = fds.flatMap((fd) => {
|
|
18387
18408
|
try {
|
|
@@ -18391,13 +18412,13 @@ function processOwners(pid) {
|
|
|
18391
18412
|
}
|
|
18392
18413
|
});
|
|
18393
18414
|
return files2.flatMap(({ fd, path }) => {
|
|
18394
|
-
if (basename3(
|
|
18415
|
+
if (basename3(dirname6(path)) !== "thread-writer-locks" || !path.endsWith(".lock")) return [];
|
|
18395
18416
|
const thread = basename3(path, ".lock");
|
|
18396
18417
|
if (!THREAD_ID.test(thread)) return [];
|
|
18397
18418
|
try {
|
|
18398
18419
|
const lock = readFileSync4(join8(root, "fdinfo", fd), "utf8");
|
|
18399
18420
|
if (!/FLOCK\s+ADVISORY\s+WRITE/.test(lock)) return [];
|
|
18400
|
-
const home = realpathSync2.native(
|
|
18421
|
+
const home = realpathSync2.native(dirname6(dirname6(path)));
|
|
18401
18422
|
const directory2 = statSync2(home);
|
|
18402
18423
|
if (process.getuid && directory2.uid !== process.getuid()) return [];
|
|
18403
18424
|
const queue = identity(join8(home, "queue_1.sqlite"));
|
|
@@ -18416,7 +18437,7 @@ function processOwners(pid) {
|
|
|
18416
18437
|
return [
|
|
18417
18438
|
{
|
|
18418
18439
|
pid,
|
|
18419
|
-
start,
|
|
18440
|
+
start: start2,
|
|
18420
18441
|
executable,
|
|
18421
18442
|
home,
|
|
18422
18443
|
thread,
|
|
@@ -18581,7 +18602,7 @@ var Writer = class {
|
|
|
18581
18602
|
this.pending.delete(id);
|
|
18582
18603
|
reject(new Error("Native queue acknowledgement was not received"));
|
|
18583
18604
|
this.close();
|
|
18584
|
-
},
|
|
18605
|
+
}, REQUEST_MS4);
|
|
18585
18606
|
this.pending.set(id, { resolve: resolve3, reject, timer });
|
|
18586
18607
|
this.process.stdin.write(JSON.stringify({ id, method, params }) + "\n");
|
|
18587
18608
|
});
|
|
@@ -19173,7 +19194,7 @@ var Connector = class {
|
|
|
19173
19194
|
for (const thread of project.threads) {
|
|
19174
19195
|
const location2 = this.discovery.locations.get(thread.id);
|
|
19175
19196
|
const harness = thread.agent?.harness;
|
|
19176
|
-
if (location2?.history && !location2.live && !project.remote && !thread.capabilities.queue && harness && harness
|
|
19197
|
+
if (location2?.history && !location2.live && !project.remote && !thread.capabilities.queue && harness && isStartHarness(harness) && this.startable.includes(harness))
|
|
19177
19198
|
thread.capabilities = { ...thread.capabilities, send: true };
|
|
19178
19199
|
}
|
|
19179
19200
|
}
|
|
@@ -19416,8 +19437,13 @@ var Connector = class {
|
|
|
19416
19437
|
return location2.live.send(command2.requestId, command2.action.text, command2.action.now);
|
|
19417
19438
|
const agent = this.attached(thread);
|
|
19418
19439
|
const saved = thread.agent;
|
|
19419
|
-
if (!agent && location2?.history && saved && saved.harness
|
|
19420
|
-
await this.harnesses.resume(
|
|
19440
|
+
if (!agent && location2?.history && saved && isStartHarness(saved.harness)) {
|
|
19441
|
+
await this.harnesses.resume(
|
|
19442
|
+
saved.harness,
|
|
19443
|
+
saved.sessionId,
|
|
19444
|
+
project.path,
|
|
19445
|
+
command2.action.text
|
|
19446
|
+
);
|
|
19421
19447
|
await this.changed();
|
|
19422
19448
|
return;
|
|
19423
19449
|
}
|
|
@@ -19491,7 +19517,7 @@ var Connector = class {
|
|
|
19491
19517
|
if (!name || name === "." || name === ".." || /[\\/:*?"<>|\x00-\x1f]/.test(name))
|
|
19492
19518
|
throw new Error("Name the folder without slashes or special characters");
|
|
19493
19519
|
cwd = join9(folder.parent, name);
|
|
19494
|
-
await
|
|
19520
|
+
await mkdir2(cwd).catch((error) => {
|
|
19495
19521
|
throw new Error(error.code === "EEXIST" ? `${name} already exists` : `Couldn't create ${name}`);
|
|
19496
19522
|
});
|
|
19497
19523
|
} else {
|
|
@@ -19521,8 +19547,8 @@ import { homedir as homedir6, userInfo } from "node:os";
|
|
|
19521
19547
|
import { promisify as promisify6 } from "node:util";
|
|
19522
19548
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19523
19549
|
import { execFile as execFile6, spawn as spawn5 } from "node:child_process";
|
|
19524
|
-
import { cp, mkdir as
|
|
19525
|
-
import { dirname as
|
|
19550
|
+
import { cp, mkdir as mkdir3, rm as rm2, writeFile } from "node:fs/promises";
|
|
19551
|
+
import { dirname as dirname7, join as join10, relative, sep } from "node:path";
|
|
19526
19552
|
var run3 = promisify6(execFile6);
|
|
19527
19553
|
var xml = (value2) => value2.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
19528
19554
|
var NODE_FLAGS = ["--disable-warning=ExperimentalWarning"];
|
|
@@ -19568,21 +19594,34 @@ async function durableEntry() {
|
|
|
19568
19594
|
);
|
|
19569
19595
|
if (!["_npx", "dlx"].some((cache2) => entry.includes(`${sep}${cache2}${sep}`))) return entry;
|
|
19570
19596
|
const root = entry.slice(0, entry.indexOf(`${sep}node_modules${sep}`));
|
|
19571
|
-
const app = join10(
|
|
19572
|
-
await
|
|
19597
|
+
const app = join10(dirname7(configPath()), "app");
|
|
19598
|
+
await rm2(app, { recursive: true, force: true });
|
|
19573
19599
|
await cp(join10(root, "node_modules"), join10(app, "node_modules"), {
|
|
19574
19600
|
recursive: true,
|
|
19575
19601
|
verbatimSymlinks: true
|
|
19576
19602
|
});
|
|
19577
19603
|
return join10(app, relative(root, entry));
|
|
19578
19604
|
}
|
|
19605
|
+
var claude2 = (...args2) => run3("claude", ["plugin", ...args2], { timeout: 6e4 });
|
|
19606
|
+
async function installClaude(entry) {
|
|
19607
|
+
if (process.platform === "win32") return false;
|
|
19608
|
+
try {
|
|
19609
|
+
await claude2("marketplace", "add", join10(dirname7(entry), "..", "claude"));
|
|
19610
|
+
await claude2("install", "dashup@dashup", "--scope", "user");
|
|
19611
|
+
return true;
|
|
19612
|
+
} catch {
|
|
19613
|
+
return false;
|
|
19614
|
+
}
|
|
19615
|
+
}
|
|
19579
19616
|
async function installStartup() {
|
|
19580
19617
|
const path = serviceFile();
|
|
19581
|
-
|
|
19582
|
-
await
|
|
19583
|
-
|
|
19584
|
-
|
|
19585
|
-
);
|
|
19618
|
+
const entry = await durableEntry();
|
|
19619
|
+
await mkdir3(dirname7(path), { recursive: true });
|
|
19620
|
+
await writeFile(path, service(process.platform, process.execPath, entry, configPath()));
|
|
19621
|
+
const restart = await installClaude(entry) ? "Restart Claude Code sessions that are already open to answer them from your phone." : void 0;
|
|
19622
|
+
return [await start(path), restart].filter(Boolean).join(" ") || void 0;
|
|
19623
|
+
}
|
|
19624
|
+
async function start(path) {
|
|
19586
19625
|
if (process.platform === "darwin") {
|
|
19587
19626
|
await run3("launchctl", ["unload", path]).catch(() => {
|
|
19588
19627
|
});
|
|
@@ -19602,13 +19641,17 @@ async function installStartup() {
|
|
|
19602
19641
|
}
|
|
19603
19642
|
async function uninstallStartup() {
|
|
19604
19643
|
const path = serviceFile();
|
|
19644
|
+
await claude2("uninstall", "dashup@dashup").catch(() => {
|
|
19645
|
+
});
|
|
19646
|
+
await claude2("marketplace", "remove", "dashup").catch(() => {
|
|
19647
|
+
});
|
|
19605
19648
|
if (process.platform === "darwin") await run3("launchctl", ["unload", path]).catch(() => {
|
|
19606
19649
|
});
|
|
19607
19650
|
else if (process.platform !== "win32")
|
|
19608
19651
|
await systemctl("disable", "dashup.service").catch(() => {
|
|
19609
19652
|
});
|
|
19610
|
-
await
|
|
19611
|
-
await
|
|
19653
|
+
await rm2(path, { force: true });
|
|
19654
|
+
await rm2(join10(dirname7(configPath()), "app"), { recursive: true, force: true });
|
|
19612
19655
|
}
|
|
19613
19656
|
|
|
19614
19657
|
// src/index.ts
|
|
@@ -19811,7 +19854,7 @@ async function main() {
|
|
|
19811
19854
|
"To pair this computer, run dashup pair in a terminal, or add --account with your Dashup email."
|
|
19812
19855
|
);
|
|
19813
19856
|
let connector;
|
|
19814
|
-
const server =
|
|
19857
|
+
const server = createServer3(async (request2, response) => {
|
|
19815
19858
|
if (!authorized(request2)) {
|
|
19816
19859
|
response.writeHead(403).end();
|
|
19817
19860
|
return;
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dashup/connector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dashup": "dist/index.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
|
-
"dist"
|
|
9
|
+
"dist",
|
|
10
|
+
"claude"
|
|
10
11
|
],
|
|
11
12
|
"publishConfig": {
|
|
12
13
|
"access": "public"
|
|
@@ -30,10 +31,10 @@
|
|
|
30
31
|
"tsx": "^4.22.4",
|
|
31
32
|
"typescript": "^5.9.3",
|
|
32
33
|
"@dashup/models": "0.1.0",
|
|
34
|
+
"@dashup/protocol": "0.1.0",
|
|
33
35
|
"@dashup/typescript-config": "0.0.0",
|
|
34
36
|
"@parcae/lynx": "0.1.0",
|
|
35
|
-
"@parcae/sdk": "0.8.1"
|
|
36
|
-
"@dashup/protocol": "0.1.0"
|
|
37
|
+
"@parcae/sdk": "0.8.1"
|
|
37
38
|
},
|
|
38
39
|
"scripts": {
|
|
39
40
|
"dashup": "tsx src/index.ts",
|