@dashup/connector 0.1.11 → 0.1.12

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 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. Direct control of an ordinary foreground Claude process is not advertised when it exposes no supported attach interface.
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, and ACP `session/cancel`. It refuses when nothing is running and expires with other commands after 60 seconds, so a late stop cannot end a newer turn.
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,5 @@
1
+ {
2
+ "name": "dashup",
3
+ "owner": { "name": "Dashup" },
4
+ "plugins": [{ "name": "dashup", "source": "./" }]
5
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "dashup",
3
+ "description": "Takes what you send this session from the Dashup app as your own words"
4
+ }
@@ -0,0 +1 @@
1
+ { "modules": ["./register.ts"] }
@@ -0,0 +1,69 @@
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.
11
+ type Delivery = { text?: string; now?: true };
12
+
13
+ const RETRY_MS = 10_000;
14
+
15
+ export const register: Register = (on) => {
16
+ let turnId: string | undefined;
17
+ let stream: AsyncGenerator<unknown, unknown> | undefined;
18
+ on('turn.start', ($, e, next) => {
19
+ if (e.agentId === undefined) turnId = e.turnId;
20
+ return next(e);
21
+ });
22
+ // `/clear` starts another conversation in this process, which streams under its own id.
23
+ on('session.end', ($, e, next) => {
24
+ void stream?.return(undefined);
25
+ return next(e);
26
+ });
27
+ on('session.start', async ($, e, next) => {
28
+ const started = await next(e);
29
+ void (async () => {
30
+ const socketPath = `${await $.env.get('HOME')}/.dashup/claude.sock`;
31
+ for (;;) {
32
+ // A request held open here would hold the session's queue; a child's stream does not.
33
+ const lines = (stream = $.process.spawn({
34
+ argv: [
35
+ 'curl',
36
+ '-sfN',
37
+ '--unix-socket',
38
+ socketPath,
39
+ `http://dashup/sessions/${await $.session.id()}`,
40
+ ],
41
+ }));
42
+ let buffer = '';
43
+ try {
44
+ for await (const { stream: name, text } of lines) {
45
+ if (name !== 'stdout') continue;
46
+ buffer += text;
47
+ for (let end = buffer.indexOf('\n'); end !== -1; end = buffer.indexOf('\n')) {
48
+ await take($, JSON.parse(buffer.slice(0, end)) as Delivery, turnId);
49
+ buffer = buffer.slice(end + 1);
50
+ }
51
+ }
52
+ } catch {
53
+ // curl is missing, or the stream broke off mid-line.
54
+ }
55
+ // Dashup is not running on this computer, or is restarting.
56
+ await $.clock.sleep(RETRY_MS);
57
+ }
58
+ })();
59
+ return started;
60
+ });
61
+ };
62
+
63
+ // A message runs once the session is idle; the stream must not wait for that.
64
+ async function take($: EngineInterface, { text, now }: Delivery, turnId?: string): Promise<void> {
65
+ if (now && turnId) await $.turn.abort({ turnId }).catch(() => {});
66
+ const command = text?.match(/^\/(\S+)\s*([\s\S]*)$/);
67
+ if (command) void $.command.run({ command: command[1]!, args: command[2] }).catch(() => {});
68
+ else if (text) void $.prompt.submit({ text, asUser: true }).catch(() => {});
69
+ }
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 start = this.lowerBound;
2152
+ const start2 = this.lowerBound;
2153
2153
  const end = this.upperBound;
2154
2154
  if (isAscending) {
2155
- for (let i = start; i < end; 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 = start; i > end; 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 createServer2 } from "node:http";
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 connect4 = request({
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
- connect4.on("connect", (response, stream) => response.statusCode === 200 ? resolve3(stream) : reject(new Error("Dashup ACP connection was rejected")));
3880
- connect4.on("error", reject);
3881
- connect4.end();
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.11";
4051
+ var version = "0.1.12";
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, start, length) {
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, start + 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 start = Math.max(head.length, stamp.size - bytes);
4278
- const buffer = readRange(path, start, stamp.size - start);
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, start - 1, 1)[0] === 10 ? buffer : buffer.subarray(buffer.indexOf(10) + 1)
4283
+ readRange(path, start2 - 1, 1)[0] === 10 ? buffer : buffer.subarray(buffer.indexOf(10) + 1)
4281
4284
  );
4282
- if (start === head.length || tail.some(until)) break;
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 start = latest ? Math.max(0, stamp.size - LATEST_BYTES) : 0;
4300
- saved = { stamp, start, offset: start, tail: Buffer.alloc(0), rows: [] };
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(start, end) {
4423
- const ms = instant(end) - instant(start);
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 start = i + 1;
7439
+ const start2 = i + 1;
7437
7440
  while (str.charAt(++i) !== "-" && i != str.length) {
7438
7441
  }
7439
- const buf = str.substring(start, i);
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 start = i + 1;
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(start, i);
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 start = i + 1;
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(start, i + 1));
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, start, size) => {
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), start);
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, start);
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, start);
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, start) {
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 - (start - strm.avail_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 start = 0;
16205
+ let start2 = 0;
16203
16206
  do {
16204
- let end = Math.min(start + HISTORY_TEXT_CHARS, message.text.length);
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(start, end));
16208
- start = end;
16209
- } while (start < message.text.length);
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 start = Math.max(0, all.length - HISTORY_KEPT);
16307
- while (start < all.length && (all[start].part ?? 0) > 0) start++;
16308
- const fragments = start ? [...historyMessages(local.key, project, thread.id, [EARLIER]), ...all.slice(start)] : all;
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 start = message.role === "tool" && !message.tool?.diff ? preview(text3) : text3;
16363
- const full = start.length < text3.length ? {
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: start,
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 start = 0;
16793
- while (start < old.length && start < next.length && old[start] === next[start]) start++;
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 - start && end < next.length - start && old[old.length - 1 - end] === next[next.length - 1 - end])
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(start, old.length - end);
16798
- const added2 = next.slice(start, next.length - end);
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 + start + 1},${removed.length} +${offset + start + 1},${added2.length} @@`,
16802
- ...old.slice(Math.max(0, start - 3), start).map((line) => ` ${line}`),
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 { lstat, readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
17089
- var STOP = "Stop here, and wait for my next message.";
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 ENVELOPE = "cross-session-message";
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 path = session.peerProtocol === PEER_PROTOCOL ? session.messagingSocketPath : void 0;
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: path ? (requestId, text3, now) => this.once(requestId, () => post(session, path, text3, now ? "now" : "next")) : void 0,
17161
- stop: path ? (requestId) => this.once(requestId, () => post(session, path, STOP, "now")) : void 0,
17162
- now: Boolean(path)
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) this.requests.delete(this.requests.keys().next().value);
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 connect3 } from "node:net";
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 dirname4, basename as basename2, isAbsolute as isAbsolute3 } from "node:path";
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 REQUEST_MS3 = 4e3;
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" ? dirname4(override) : override] : [];
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: REQUEST_MS3, maxBuffer: MAX_METADATA }
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(dirname4(path), MAX_BYTES2)) continue;
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: () => connect3(this.entry.path),
17468
+ createConnection: () => connect2(this.entry.path),
17452
17469
  maxPayload: MAX_BYTES2,
17453
- handshakeTimeout: REQUEST_MS3
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: REQUEST_MS3,
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
- }, REQUEST_MS3);
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 REQUEST_MS4 = 4e3;
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: REQUEST_MS4, maxBuffer: MAX_BYTES3 }
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: REQUEST_MS4, maxBuffer: MAX_BYTES3 }
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(REQUEST_MS4);
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(), REQUEST_MS4);
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 dirname5, basename as basename3 } from "node:path";
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 REQUEST_MS5 = 1e4;
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 = dirname5(executable);
18369
- for (let depth = 0; depth < 8; depth++, directory2 = dirname5(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 start = status2.slice(status2.lastIndexOf(")") + 2).split(" ")[19];
18384
- if (!start || !/^\d+$/.test(start)) return [];
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(dirname5(path)) !== "thread-writer-locks" || !path.endsWith(".lock")) return [];
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(dirname5(dirname5(path)));
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
- }, REQUEST_MS5);
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 !== "other" && this.startable.includes(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 !== "other") {
19420
- await this.harnesses.resume(saved.harness, saved.sessionId, project.path, command2.action.text);
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 mkdir(cwd).catch((error) => {
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 mkdir2, rm, writeFile } from "node:fs/promises";
19525
- import { dirname as dirname6, join as join10, relative, sep } from "node:path";
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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
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(dirname6(configPath()), "app");
19572
- await rm(app, { recursive: true, force: true });
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
- await mkdir2(dirname6(path), { recursive: true });
19582
- await writeFile(
19583
- path,
19584
- service(process.platform, process.execPath, await durableEntry(), configPath())
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 rm(path, { force: true });
19611
- await rm(join10(dirname6(configPath()), "app"), { recursive: true, force: true });
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 = createServer2(async (request2, response) => {
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.11",
3
+ "version": "0.1.12",
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,9 +31,9 @@
30
31
  "tsx": "^4.22.4",
31
32
  "typescript": "^5.9.3",
32
33
  "@dashup/models": "0.1.0",
33
- "@dashup/typescript-config": "0.0.0",
34
34
  "@parcae/lynx": "0.1.0",
35
35
  "@parcae/sdk": "0.8.1",
36
+ "@dashup/typescript-config": "0.0.0",
36
37
  "@dashup/protocol": "0.1.0"
37
38
  },
38
39
  "scripts": {