@dashup/connector 0.1.13 → 0.1.14
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 +3 -1
- package/dist/index.js +292 -162
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ dashup alert --project PROJECT_ID --title "Dashup is connected"
|
|
|
22
22
|
|
|
23
23
|
The connector automatically reads existing Claude Code, Codex, and OpenCode conversations alongside Zed workspaces and VS Code chat history. It honors the harness's existing data directory, authentication, and native session IDs. Native stores are opened read-only; discovery never launches an agent, creates or resumes a conversation, or changes editor settings. Only a session the phone starts runs an agent (see below). Opening the same supported harness session through Zed and its native history uses one Dashup thread when their actual session identities match. Matching directory names alone never merges local and SSH projects.
|
|
24
24
|
|
|
25
|
-
A thread's title is the agent's own (a rename, Codex's stored title, Claude's title), else the first message the person typed: Codex's injected AGENTS.md and environment messages and Claude Code's slash commands are skipped,
|
|
25
|
+
A thread's title is the agent's own (a rename, Codex's stored title, Claude's title), else the first message the person typed: Codex's injected AGENTS.md and environment messages and Claude Code's slash commands are skipped, and a Codex subagent is named for its task. In the conversation, Codex's injected messages and Claude Code's own meta rows stay out, and a Claude Code slash command reads as typed, followed by its output. A thread's time is its last conversation message in the agent's own history; editor bookkeeping such as Zed's sidebar restamps never moves it. Subagent sessions (OpenCode children, Codex spawned agents) carry their parent thread. Tool calls are joined with their results into one step with status, duration and, for file edits, a unified diff. Saved history has unknown live status; the phone can answer it by resuming it (see Starting sessions from the phone). File changes publish project metadata; only an approval, a question, an error or a finished turn sends an alert. A workspace becomes a project once it has a conversation. After every change the connector publishes its overview: every project, and the conversations Home shows (anything that needs the person or is working, then the newest 20). The phone renders Home from the overview alone and decodes a project's full snapshot only when a page opens it. Only a scan that read every source retires a project it published earlier and left off. Each phone read request reads a thread's native history once and publishes a snapshot of separately encrypted message fragments to the database. The phone reads bounded latest and earlier pages; realtime thread updates carry only summary and approval metadata. Native history remains the source, and an incomplete upload never replaces the previous published snapshot. Native live interfaces provide actual status and pending choices. Tool integrations can also queue explicit encrypted alerts with `alert`.
|
|
26
26
|
|
|
27
27
|
Project paths, SSH details, thread content, approvals, and alerts are encrypted separately for every paired phone. The encrypted outbox survives a connector restart. Parcae handles realtime delivery and reconnect; replay counters are saved before commands execute. Revoking a pairing discards its connector key and queued content.
|
|
28
28
|
|
|
@@ -47,6 +47,8 @@ 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
|
+
A running Claude Code session shows the model its newest reply ran on and, where that model takes them, fast mode and ultracode, as its transcript records them. The choices and their names are the models `claude` offers hosts in its initialize answer. The phone changes them through the plugin with Claude Code's own `/model`, `/fast` and `/effort ultracode` commands, and in a session the phone started with control requests at the start of each turn. Claude Code records a change when the next turn starts, so it shows from the next reply. A change expires with other commands after 60 seconds.
|
|
51
|
+
|
|
50
52
|
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
53
|
|
|
52
54
|
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.
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
42
42
|
"use strict";
|
|
43
43
|
var fs = __require("fs");
|
|
44
44
|
var Url = __require("url");
|
|
45
|
-
var
|
|
45
|
+
var spawn7 = __require("child_process").spawn;
|
|
46
46
|
module.exports = XMLHttpRequest3;
|
|
47
47
|
XMLHttpRequest3.XMLHttpRequest = XMLHttpRequest3;
|
|
48
48
|
function XMLHttpRequest3(opts) {
|
|
@@ -338,7 +338,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
338
338
|
var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
|
|
339
339
|
fs.writeFileSync(syncFile, "", "utf8");
|
|
340
340
|
var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
|
|
341
|
-
var syncProc =
|
|
341
|
+
var syncProc = spawn7(process.argv[0], ["-e", execString]);
|
|
342
342
|
var statusText;
|
|
343
343
|
while (fs.existsSync(syncFile)) {
|
|
344
344
|
}
|
|
@@ -4048,7 +4048,7 @@ import * as ui from "@clack/prompts";
|
|
|
4048
4048
|
import { z } from "zod";
|
|
4049
4049
|
|
|
4050
4050
|
// package.json
|
|
4051
|
-
var version = "0.1.
|
|
4051
|
+
var version = "0.1.14";
|
|
4052
4052
|
|
|
4053
4053
|
// src/pair.ts
|
|
4054
4054
|
var Invitation = z.object({
|
|
@@ -4459,6 +4459,13 @@ function step(id, name, input, result, started) {
|
|
|
4459
4459
|
}
|
|
4460
4460
|
var claudeHome = () => process.env.CLAUDE_CONFIG_DIR ?? join2(homedir2(), ".claude");
|
|
4461
4461
|
var claudeMessage = (row) => (row.type === "user" || row.type === "assistant") && !row.isSidechain;
|
|
4462
|
+
var OUTPUT_TAGS = /<\/?local-command-std\w+>/g;
|
|
4463
|
+
var claudeOutput = (row) => row.type === "system" && row.subtype === "local_command" ? text(row.content).replace(OUTPUT_TAGS, "").trim() : void 0;
|
|
4464
|
+
function claudeCommand(value2) {
|
|
4465
|
+
const name = /<command-name>([^<]*)<\/command-name>/.exec(value2)?.[1];
|
|
4466
|
+
const args2 = /<command-args>([^<]*)<\/command-args>/.exec(value2)?.[1] ?? "";
|
|
4467
|
+
return name === void 0 ? void 0 : `${name} ${args2}`.trim();
|
|
4468
|
+
}
|
|
4462
4469
|
function claudeChange(change2) {
|
|
4463
4470
|
if (typeof change2?.filePath !== "string") return void 0;
|
|
4464
4471
|
const hunks = list(change2.structuredPatch);
|
|
@@ -4480,7 +4487,8 @@ function claudeMessages(path) {
|
|
|
4480
4487
|
for (const row of rows) {
|
|
4481
4488
|
if (typeof row.uuid === "string" && !row.isSidechain) {
|
|
4482
4489
|
chain.set(row.uuid, row);
|
|
4483
|
-
if (row.type === "user" || row.type === "assistant")
|
|
4490
|
+
if (row.type === "user" || row.type === "assistant" || claudeOutput(row) !== void 0)
|
|
4491
|
+
leaf = row.uuid;
|
|
4484
4492
|
}
|
|
4485
4493
|
if (typeof row.leafUuid === "string" && chain.has(row.leafUuid)) leaf = row.leafUuid;
|
|
4486
4494
|
}
|
|
@@ -4502,9 +4510,13 @@ function claudeConversation(conversation, rows) {
|
|
|
4502
4510
|
for (const part of row.type === "user" ? list(row.message?.content) : [])
|
|
4503
4511
|
if (part?.type === "tool_result") results.set(part.tool_use_id, { part, row });
|
|
4504
4512
|
return conversation.flatMap((row, index) => {
|
|
4505
|
-
if (row.type !== "user" && row.type !== "assistant") return [];
|
|
4506
4513
|
const id = String(row.uuid ?? row.message?.id ?? index);
|
|
4507
4514
|
const content = row.message?.content;
|
|
4515
|
+
const output = claudeOutput(row);
|
|
4516
|
+
const command2 = row.type === "user" ? claudeCommand(text(content)) : void 0;
|
|
4517
|
+
if (output !== void 0) return prose(id, "system", output);
|
|
4518
|
+
if (row.type !== "user" && row.type !== "assistant" || row.isMeta) return [];
|
|
4519
|
+
if (command2 !== void 0) return [{ id, role: "user", text: command2 }];
|
|
4508
4520
|
return [
|
|
4509
4521
|
...prose(id, row.type, content),
|
|
4510
4522
|
...list(content).flatMap((part, position) => {
|
|
@@ -4530,6 +4542,23 @@ function claudeConversation(conversation, rows) {
|
|
|
4530
4542
|
];
|
|
4531
4543
|
});
|
|
4532
4544
|
}
|
|
4545
|
+
function claudeSettings(path) {
|
|
4546
|
+
const rows = journal(path, true);
|
|
4547
|
+
const settings = {};
|
|
4548
|
+
for (let index = rows.length - 1; index >= 0; index--) {
|
|
4549
|
+
const row = rows[index];
|
|
4550
|
+
const message = object(row.message);
|
|
4551
|
+
const marker = row.type === "attachment" ? object(row.attachment)?.type : void 0;
|
|
4552
|
+
if (settings.model === void 0 && row.type === "assistant" && !row.isSidechain && typeof message?.model === "string" && message.model !== "<synthetic>") {
|
|
4553
|
+
settings.model = message.model;
|
|
4554
|
+
settings.fast = object(message.usage)?.speed === "fast";
|
|
4555
|
+
}
|
|
4556
|
+
if (settings.ultracode === void 0 && (marker === "ultra_effort_enter" || marker === "ultra_effort_exit"))
|
|
4557
|
+
settings.ultracode = marker === "ultra_effort_enter";
|
|
4558
|
+
if (settings.model !== void 0 && settings.ultracode !== void 0) break;
|
|
4559
|
+
}
|
|
4560
|
+
return { ultracode: false, ...settings };
|
|
4561
|
+
}
|
|
4533
4562
|
function claude(root, scan) {
|
|
4534
4563
|
for (const path of files(join2(root, "projects"), scan, 1).filter((file) => file.endsWith(".jsonl"))) {
|
|
4535
4564
|
try {
|
|
@@ -4559,7 +4588,8 @@ function claude(root, scan) {
|
|
|
4559
4588
|
harness: "claude",
|
|
4560
4589
|
...session,
|
|
4561
4590
|
files: [path],
|
|
4562
|
-
read: () => claudeMessages(path)
|
|
4591
|
+
read: () => claudeMessages(path),
|
|
4592
|
+
settings: () => claudeSettings(path)
|
|
4563
4593
|
});
|
|
4564
4594
|
} catch {
|
|
4565
4595
|
scan.warnings.push(`Cannot read Claude Code session: ${path}`);
|
|
@@ -4748,8 +4778,8 @@ function databaseMessages(path, session) {
|
|
|
4748
4778
|
}
|
|
4749
4779
|
}
|
|
4750
4780
|
function codexSubagent(row) {
|
|
4751
|
-
const
|
|
4752
|
-
const task = typeof
|
|
4781
|
+
const spawn7 = object(object(parse(String(row.source ?? "")))?.subagent)?.thread_spawn;
|
|
4782
|
+
const task = typeof spawn7?.agent_path === "string" ? spawn7.agent_path.split("/").pop() : "";
|
|
4753
4783
|
return [row.agent_nickname, task?.replace(/_/g, " ")].filter(Boolean).join(" \xB7 ");
|
|
4754
4784
|
}
|
|
4755
4785
|
var codexState;
|
|
@@ -5069,13 +5099,15 @@ var Discovery = class {
|
|
|
5069
5099
|
title: session.title ?? found.thread.title,
|
|
5070
5100
|
status: session.status,
|
|
5071
5101
|
updatedAt: location2.history?.updatedAt ?? session.updatedAt,
|
|
5102
|
+
options: session.options?.(location2.history?.settings?.() ?? {}),
|
|
5072
5103
|
capabilities: {
|
|
5073
5104
|
...found.thread.capabilities,
|
|
5074
5105
|
read: true,
|
|
5075
5106
|
send: Boolean(session.send),
|
|
5076
5107
|
stop: Boolean(session.stop),
|
|
5077
5108
|
now: Boolean(session.now),
|
|
5078
|
-
approve: Boolean(session.approve)
|
|
5109
|
+
approve: Boolean(session.approve),
|
|
5110
|
+
configure: Boolean(session.configure)
|
|
5079
5111
|
}
|
|
5080
5112
|
});
|
|
5081
5113
|
return found;
|
|
@@ -13214,22 +13246,22 @@ var FrontendAdapter = class {
|
|
|
13214
13246
|
if (options.subscribe === false) requestData.__subscribe = false;
|
|
13215
13247
|
const result = await this.transport.get(path, requestData);
|
|
13216
13248
|
const items = result?.[(0, import_pluralize.default)(modelClass.type)] ?? result?.[modelClass.type + "s"] ?? result?.items ?? [];
|
|
13217
|
-
const
|
|
13249
|
+
const models2 = items.map(
|
|
13218
13250
|
(row) => modelClass.hydrate(this, row)
|
|
13219
13251
|
);
|
|
13220
13252
|
if (result?.__queryHash) {
|
|
13221
|
-
Object.defineProperty(
|
|
13253
|
+
Object.defineProperty(models2, "__queryHash", {
|
|
13222
13254
|
value: result.__queryHash,
|
|
13223
13255
|
enumerable: false
|
|
13224
13256
|
});
|
|
13225
13257
|
}
|
|
13226
13258
|
if (typeof result?.totalCount === "number") {
|
|
13227
|
-
Object.defineProperty(
|
|
13259
|
+
Object.defineProperty(models2, "__totalCount", {
|
|
13228
13260
|
value: result.totalCount,
|
|
13229
13261
|
enumerable: false
|
|
13230
13262
|
});
|
|
13231
13263
|
}
|
|
13232
|
-
return
|
|
13264
|
+
return models2;
|
|
13233
13265
|
};
|
|
13234
13266
|
chain.first = async () => {
|
|
13235
13267
|
const all = await chain.find();
|
|
@@ -16780,15 +16812,206 @@ var Codex = class {
|
|
|
16780
16812
|
// src/harness/headless.ts
|
|
16781
16813
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
16782
16814
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
16783
|
-
import { createInterface as
|
|
16815
|
+
import { createInterface as createInterface3 } from "node:readline";
|
|
16816
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
16817
|
+
|
|
16818
|
+
// src/harness/claude.ts
|
|
16819
|
+
import { homedir as homedir5 } from "node:os";
|
|
16820
|
+
import { dirname as dirname4, join as join5 } from "node:path";
|
|
16784
16821
|
import { spawn as spawn2 } from "node:child_process";
|
|
16822
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
16823
|
+
import { watch as watch2 } from "node:fs";
|
|
16824
|
+
import { mkdir, readdir as readdir2, readFile as readFile2, rm } from "node:fs/promises";
|
|
16825
|
+
import { createServer as createServer2 } from "node:http";
|
|
16826
|
+
var MODELS_MS = 3e4;
|
|
16827
|
+
var COMMANDS = {
|
|
16828
|
+
model: (value2) => `/model ${value2}`,
|
|
16829
|
+
fast: (value2) => `/fast ${value2 ? "on" : "off"}`,
|
|
16830
|
+
ultracode: (value2) => `/effort ultracode ${value2 ? "on" : "off"}`
|
|
16831
|
+
};
|
|
16832
|
+
var models = [];
|
|
16833
|
+
function askModels() {
|
|
16834
|
+
return new Promise((resolve3) => {
|
|
16835
|
+
const child = spawn2(
|
|
16836
|
+
"claude",
|
|
16837
|
+
["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"],
|
|
16838
|
+
{ cwd: homedir5(), stdio: "pipe", shell: process.platform === "win32" }
|
|
16839
|
+
);
|
|
16840
|
+
const timer = setTimeout(() => child.kill(), MODELS_MS);
|
|
16841
|
+
child.stdin.on("error", () => {
|
|
16842
|
+
});
|
|
16843
|
+
child.once("error", () => resolve3());
|
|
16844
|
+
child.once("exit", () => {
|
|
16845
|
+
clearTimeout(timer);
|
|
16846
|
+
resolve3();
|
|
16847
|
+
});
|
|
16848
|
+
createInterface2({ input: child.stdout }).on("line", (line) => {
|
|
16849
|
+
try {
|
|
16850
|
+
const message = JSON.parse(line);
|
|
16851
|
+
if (message?.type !== "control_response" || message.response?.request_id !== "models")
|
|
16852
|
+
return;
|
|
16853
|
+
const offered = message.response.response?.models;
|
|
16854
|
+
if (Array.isArray(offered))
|
|
16855
|
+
models = offered.filter(
|
|
16856
|
+
(model) => typeof model?.value === "string" && typeof model.displayName === "string"
|
|
16857
|
+
);
|
|
16858
|
+
child.stdin.end();
|
|
16859
|
+
} catch {
|
|
16860
|
+
}
|
|
16861
|
+
});
|
|
16862
|
+
child.stdin.write(
|
|
16863
|
+
`${JSON.stringify({ type: "control_request", request_id: "models", request: { subtype: "initialize" } })}
|
|
16864
|
+
`
|
|
16865
|
+
);
|
|
16866
|
+
});
|
|
16867
|
+
}
|
|
16868
|
+
function claudeOptions(recorded) {
|
|
16869
|
+
const ran = typeof recorded.model === "string" ? recorded.model : "";
|
|
16870
|
+
const current = models.find((model) => model.value !== "default" && model.resolvedModel === ran) ?? models.find((model) => model.value === ran);
|
|
16871
|
+
const choices = models.map((model) => ({ value: model.value, name: model.displayName }));
|
|
16872
|
+
if (ran && !current) choices.push({ value: ran, name: ran });
|
|
16873
|
+
if (!choices.length) return [];
|
|
16874
|
+
return [
|
|
16875
|
+
{ id: "model", name: "Model", value: current?.value ?? ran, choices },
|
|
16876
|
+
...current?.supportsFastMode ? [{ id: "fast", name: "Fast", value: recorded.fast === true }] : [],
|
|
16877
|
+
...current?.supportedEffortLevels?.includes("xhigh") ? [{ id: "ultracode", name: "Ultracode", value: recorded.ultracode === true }] : []
|
|
16878
|
+
];
|
|
16879
|
+
}
|
|
16880
|
+
var MAX_SESSIONS2 = 100;
|
|
16881
|
+
var MAX_REQUESTS = 1e3;
|
|
16882
|
+
var SESSION = /^\/sessions\/([\w-]{1,100})$/;
|
|
16883
|
+
var STATUS = {
|
|
16884
|
+
busy: "working",
|
|
16885
|
+
idle: "idle",
|
|
16886
|
+
shell: "idle",
|
|
16887
|
+
waiting: "attention"
|
|
16888
|
+
};
|
|
16889
|
+
async function registrations() {
|
|
16890
|
+
if (process.env.DASHUP_LIVE_DISCOVERY === "0") return [];
|
|
16891
|
+
const directory2 = join5(claudeHome(), "sessions");
|
|
16892
|
+
const names = await readdir2(directory2).catch(() => []);
|
|
16893
|
+
const found = await Promise.all(
|
|
16894
|
+
names.filter((name) => /^\d+\.json$/.test(name)).slice(0, MAX_SESSIONS2).map(async (name) => {
|
|
16895
|
+
try {
|
|
16896
|
+
const value2 = JSON.parse(await readFile2(join5(directory2, name), "utf8"));
|
|
16897
|
+
if (typeof value2.sessionId !== "string" || typeof value2.cwd !== "string") return [];
|
|
16898
|
+
process.kill(value2.pid, 0);
|
|
16899
|
+
return [value2];
|
|
16900
|
+
} catch {
|
|
16901
|
+
return [];
|
|
16902
|
+
}
|
|
16903
|
+
})
|
|
16904
|
+
);
|
|
16905
|
+
return found.flat();
|
|
16906
|
+
}
|
|
16907
|
+
var Claude = class {
|
|
16908
|
+
constructor(changed, socket = join5(dirname4(configPath()), "claude.sock")) {
|
|
16909
|
+
this.changed = changed;
|
|
16910
|
+
this.socket = socket;
|
|
16911
|
+
}
|
|
16912
|
+
changed;
|
|
16913
|
+
socket;
|
|
16914
|
+
watcher;
|
|
16915
|
+
notify;
|
|
16916
|
+
server;
|
|
16917
|
+
streams = /* @__PURE__ */ new Map();
|
|
16918
|
+
requests = /* @__PURE__ */ new Map();
|
|
16919
|
+
async refresh() {
|
|
16920
|
+
this.watch();
|
|
16921
|
+
return (await registrations()).map((session) => {
|
|
16922
|
+
const plugin = this.streams.has(session.sessionId);
|
|
16923
|
+
return {
|
|
16924
|
+
id: session.sessionId,
|
|
16925
|
+
harness: "claude",
|
|
16926
|
+
cwd: session.cwd,
|
|
16927
|
+
updatedAt: new Date(session.updatedAt ?? 0).toISOString(),
|
|
16928
|
+
status: STATUS[session.status ?? ""] ?? "unknown",
|
|
16929
|
+
messages: [],
|
|
16930
|
+
approvals: [],
|
|
16931
|
+
send: plugin ? (requestId, text3, now) => this.once(requestId, () => this.deliver(session.sessionId, now ? { text: text3, now } : { text: text3 })) : void 0,
|
|
16932
|
+
stop: plugin ? (requestId) => this.once(requestId, () => this.deliver(session.sessionId, { now: true })) : void 0,
|
|
16933
|
+
now: plugin,
|
|
16934
|
+
options: claudeOptions,
|
|
16935
|
+
configure: plugin ? (requestId, option2, value2) => this.once(requestId, () => this.deliver(session.sessionId, { text: COMMANDS[option2](value2) })) : void 0
|
|
16936
|
+
};
|
|
16937
|
+
});
|
|
16938
|
+
}
|
|
16939
|
+
// Only the connector serves the plugin; one-off commands such as `dashup doctor` read alone.
|
|
16940
|
+
async listen() {
|
|
16941
|
+
if (this.server || process.env.DASHUP_LIVE_DISCOVERY === "0") return;
|
|
16942
|
+
const server = this.server = createServer2((request2, response) => {
|
|
16943
|
+
const id = request2.method === "GET" ? SESSION.exec(request2.url ?? "")?.[1] : void 0;
|
|
16944
|
+
if (!id) return void response.writeHead(404).end();
|
|
16945
|
+
this.streams.get(id)?.end();
|
|
16946
|
+
this.streams.set(id, response);
|
|
16947
|
+
response.once("close", () => {
|
|
16948
|
+
if (this.streams.get(id) === response) this.streams.delete(id);
|
|
16949
|
+
this.changed();
|
|
16950
|
+
});
|
|
16951
|
+
response.writeHead(200, { "Content-Type": "application/x-ndjson" }).flushHeaders();
|
|
16952
|
+
this.changed();
|
|
16953
|
+
});
|
|
16954
|
+
await mkdir(dirname4(this.socket), { recursive: true, mode: 448 });
|
|
16955
|
+
await rm(this.socket, { force: true });
|
|
16956
|
+
if (this.server !== server) return;
|
|
16957
|
+
await new Promise((resolve3, reject) => {
|
|
16958
|
+
server.once("error", reject);
|
|
16959
|
+
server.listen(this.socket, resolve3);
|
|
16960
|
+
});
|
|
16961
|
+
}
|
|
16962
|
+
deliver(id, delivery) {
|
|
16963
|
+
const stream = this.streams.get(id);
|
|
16964
|
+
if (!stream) return Promise.reject(new Error("Claude Code is not taking messages"));
|
|
16965
|
+
return new Promise((resolve3, reject) => stream.write(`${JSON.stringify(delivery)}
|
|
16966
|
+
`, (error) => error ? reject(error) : resolve3()));
|
|
16967
|
+
}
|
|
16968
|
+
// Each request delivers at most once.
|
|
16969
|
+
once(id, action) {
|
|
16970
|
+
const known = this.requests.get(id);
|
|
16971
|
+
if (known) return known;
|
|
16972
|
+
if (this.requests.size >= MAX_REQUESTS)
|
|
16973
|
+
this.requests.delete(this.requests.keys().next().value);
|
|
16974
|
+
const result = action();
|
|
16975
|
+
this.requests.set(id, result);
|
|
16976
|
+
return result;
|
|
16977
|
+
}
|
|
16978
|
+
watch() {
|
|
16979
|
+
if (this.watcher || process.env.DASHUP_LIVE_DISCOVERY === "0") return;
|
|
16980
|
+
try {
|
|
16981
|
+
const watcher = this.watcher = watch2(join5(claudeHome(), "sessions"), () => {
|
|
16982
|
+
if (this.notify) return;
|
|
16983
|
+
this.notify = setTimeout(() => {
|
|
16984
|
+
this.notify = void 0;
|
|
16985
|
+
this.changed();
|
|
16986
|
+
}, 100);
|
|
16987
|
+
this.notify.unref();
|
|
16988
|
+
});
|
|
16989
|
+
watcher.on("error", () => {
|
|
16990
|
+
watcher.close();
|
|
16991
|
+
if (this.watcher === watcher) this.watcher = void 0;
|
|
16992
|
+
});
|
|
16993
|
+
} catch {
|
|
16994
|
+
}
|
|
16995
|
+
}
|
|
16996
|
+
stop() {
|
|
16997
|
+
clearTimeout(this.notify);
|
|
16998
|
+
this.watcher?.close();
|
|
16999
|
+
this.watcher = void 0;
|
|
17000
|
+
this.server?.closeAllConnections();
|
|
17001
|
+
this.server?.close();
|
|
17002
|
+
this.server = void 0;
|
|
17003
|
+
}
|
|
17004
|
+
};
|
|
17005
|
+
|
|
17006
|
+
// src/harness/headless.ts
|
|
16785
17007
|
var CHOICES = [
|
|
16786
17008
|
{ id: "accept", label: "Allow once" },
|
|
16787
17009
|
{ id: "decline", label: "Reject" }
|
|
16788
17010
|
];
|
|
16789
17011
|
var EDITS = /* @__PURE__ */ new Set(["Edit", "MultiEdit", "NotebookEdit", "Write"]);
|
|
16790
|
-
var
|
|
17012
|
+
var MAX_REQUESTS2 = 1e3;
|
|
16791
17013
|
var NOTIFY_MS = 100;
|
|
17014
|
+
var SWITCHES = { fast: "fastMode", ultracode: "ultracode" };
|
|
16792
17015
|
function hunk(before, after, offset = 0) {
|
|
16793
17016
|
const old = before ? before.replace(/\n$/, "").split("\n") : [];
|
|
16794
17017
|
const next = after ? after.replace(/\n$/, "").split("\n") : [];
|
|
@@ -16881,6 +17104,8 @@ var Session = class {
|
|
|
16881
17104
|
typing = "";
|
|
16882
17105
|
approvals = /* @__PURE__ */ new Map();
|
|
16883
17106
|
requests = /* @__PURE__ */ new Map();
|
|
17107
|
+
// What the person chose for this session, applied to each turn as it starts.
|
|
17108
|
+
chosen = {};
|
|
16884
17109
|
write(value2) {
|
|
16885
17110
|
this.turn?.stdin.write(`${JSON.stringify(value2)}
|
|
16886
17111
|
`);
|
|
@@ -16892,10 +17117,17 @@ var Session = class {
|
|
|
16892
17117
|
request: { subtype: "interrupt" }
|
|
16893
17118
|
});
|
|
16894
17119
|
}
|
|
17120
|
+
apply(option2, value2) {
|
|
17121
|
+
this.write({
|
|
17122
|
+
type: "control_request",
|
|
17123
|
+
request_id: randomUUID3(),
|
|
17124
|
+
request: option2 === "model" ? { subtype: "set_model", model: value2 } : { subtype: "apply_flag_settings", settings: { [SWITCHES[option2]]: value2 } }
|
|
17125
|
+
});
|
|
17126
|
+
}
|
|
16895
17127
|
once(id, action) {
|
|
16896
17128
|
const known = this.requests.get(id);
|
|
16897
17129
|
if (known) return known;
|
|
16898
|
-
if (this.requests.size >=
|
|
17130
|
+
if (this.requests.size >= MAX_REQUESTS2)
|
|
16899
17131
|
throw new Error("Claude Code command history is full; restart Dashup before sending");
|
|
16900
17132
|
const result = action();
|
|
16901
17133
|
this.requests.set(id, result);
|
|
@@ -16916,7 +17148,7 @@ var Session = class {
|
|
|
16916
17148
|
this.changed();
|
|
16917
17149
|
return Promise.resolve();
|
|
16918
17150
|
}
|
|
16919
|
-
const turn = this.turn =
|
|
17151
|
+
const turn = this.turn = spawn3(
|
|
16920
17152
|
"claude",
|
|
16921
17153
|
[
|
|
16922
17154
|
"-p",
|
|
@@ -16940,7 +17172,7 @@ var Session = class {
|
|
|
16940
17172
|
errors = (errors + String(chunk)).slice(-2e3);
|
|
16941
17173
|
});
|
|
16942
17174
|
const started = new Promise((resolve3, reject) => {
|
|
16943
|
-
|
|
17175
|
+
createInterface3({ input: turn.stdout }).on("line", (line) => {
|
|
16944
17176
|
if (this.receive(line)) resolve3();
|
|
16945
17177
|
});
|
|
16946
17178
|
const end = (reason) => {
|
|
@@ -16955,6 +17187,7 @@ var Session = class {
|
|
|
16955
17187
|
turn.once("error", () => end("Claude Code is not installed on this computer"));
|
|
16956
17188
|
turn.once("exit", () => end(errors.trim() || "Claude Code stopped before starting"));
|
|
16957
17189
|
});
|
|
17190
|
+
for (const [option2, value2] of Object.entries(this.chosen)) this.apply(option2, value2);
|
|
16958
17191
|
this.write(message);
|
|
16959
17192
|
this.changed();
|
|
16960
17193
|
return started;
|
|
@@ -17009,6 +17242,11 @@ var Session = class {
|
|
|
17009
17242
|
approvals: [...this.approvals.values()].map((pending) => pending.approval),
|
|
17010
17243
|
send: (requestId, text3, now) => this.once(requestId, () => this.say(text3, now)),
|
|
17011
17244
|
now: true,
|
|
17245
|
+
options: claudeOptions,
|
|
17246
|
+
configure: (requestId, option2, value2) => this.once(requestId, async () => {
|
|
17247
|
+
this.chosen[option2] = value2;
|
|
17248
|
+
this.apply(option2, value2);
|
|
17249
|
+
}),
|
|
17012
17250
|
stop: this.turn ? (requestId) => this.once(requestId, async () => {
|
|
17013
17251
|
if (!this.turn) throw new Error("Nothing is running in this Claude Code session");
|
|
17014
17252
|
this.interrupt();
|
|
@@ -17084,138 +17322,9 @@ var Headless = class {
|
|
|
17084
17322
|
}
|
|
17085
17323
|
};
|
|
17086
17324
|
|
|
17087
|
-
// src/harness/claude.ts
|
|
17088
|
-
import { dirname as dirname4, join as join5 } from "node:path";
|
|
17089
|
-
import { watch as watch2 } from "node:fs";
|
|
17090
|
-
import { mkdir, readdir as readdir2, readFile as readFile2, rm } from "node:fs/promises";
|
|
17091
|
-
import { createServer as createServer2 } from "node:http";
|
|
17092
|
-
var MAX_SESSIONS2 = 100;
|
|
17093
|
-
var MAX_REQUESTS2 = 1e3;
|
|
17094
|
-
var SESSION = /^\/sessions\/([\w-]{1,100})$/;
|
|
17095
|
-
var STATUS = {
|
|
17096
|
-
busy: "working",
|
|
17097
|
-
idle: "idle",
|
|
17098
|
-
shell: "idle",
|
|
17099
|
-
waiting: "attention"
|
|
17100
|
-
};
|
|
17101
|
-
async function registrations() {
|
|
17102
|
-
if (process.env.DASHUP_LIVE_DISCOVERY === "0") return [];
|
|
17103
|
-
const directory2 = join5(claudeHome(), "sessions");
|
|
17104
|
-
const names = await readdir2(directory2).catch(() => []);
|
|
17105
|
-
const found = await Promise.all(
|
|
17106
|
-
names.filter((name) => /^\d+\.json$/.test(name)).slice(0, MAX_SESSIONS2).map(async (name) => {
|
|
17107
|
-
try {
|
|
17108
|
-
const value2 = JSON.parse(await readFile2(join5(directory2, name), "utf8"));
|
|
17109
|
-
if (typeof value2.sessionId !== "string" || typeof value2.cwd !== "string") return [];
|
|
17110
|
-
process.kill(value2.pid, 0);
|
|
17111
|
-
return [value2];
|
|
17112
|
-
} catch {
|
|
17113
|
-
return [];
|
|
17114
|
-
}
|
|
17115
|
-
})
|
|
17116
|
-
);
|
|
17117
|
-
return found.flat();
|
|
17118
|
-
}
|
|
17119
|
-
var Claude = class {
|
|
17120
|
-
constructor(changed, socket = join5(dirname4(configPath()), "claude.sock")) {
|
|
17121
|
-
this.changed = changed;
|
|
17122
|
-
this.socket = socket;
|
|
17123
|
-
}
|
|
17124
|
-
changed;
|
|
17125
|
-
socket;
|
|
17126
|
-
watcher;
|
|
17127
|
-
notify;
|
|
17128
|
-
server;
|
|
17129
|
-
streams = /* @__PURE__ */ new Map();
|
|
17130
|
-
requests = /* @__PURE__ */ new Map();
|
|
17131
|
-
async refresh() {
|
|
17132
|
-
this.watch();
|
|
17133
|
-
return (await registrations()).map((session) => {
|
|
17134
|
-
const plugin = this.streams.has(session.sessionId);
|
|
17135
|
-
return {
|
|
17136
|
-
id: session.sessionId,
|
|
17137
|
-
harness: "claude",
|
|
17138
|
-
cwd: session.cwd,
|
|
17139
|
-
updatedAt: new Date(session.updatedAt ?? 0).toISOString(),
|
|
17140
|
-
status: STATUS[session.status ?? ""] ?? "unknown",
|
|
17141
|
-
messages: [],
|
|
17142
|
-
approvals: [],
|
|
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
|
|
17146
|
-
};
|
|
17147
|
-
});
|
|
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.
|
|
17179
|
-
once(id, action) {
|
|
17180
|
-
const known = this.requests.get(id);
|
|
17181
|
-
if (known) return known;
|
|
17182
|
-
if (this.requests.size >= MAX_REQUESTS2)
|
|
17183
|
-
this.requests.delete(this.requests.keys().next().value);
|
|
17184
|
-
const result = action();
|
|
17185
|
-
this.requests.set(id, result);
|
|
17186
|
-
return result;
|
|
17187
|
-
}
|
|
17188
|
-
watch() {
|
|
17189
|
-
if (this.watcher || process.env.DASHUP_LIVE_DISCOVERY === "0") return;
|
|
17190
|
-
try {
|
|
17191
|
-
const watcher = this.watcher = watch2(join5(claudeHome(), "sessions"), () => {
|
|
17192
|
-
if (this.notify) return;
|
|
17193
|
-
this.notify = setTimeout(() => {
|
|
17194
|
-
this.notify = void 0;
|
|
17195
|
-
this.changed();
|
|
17196
|
-
}, 100);
|
|
17197
|
-
this.notify.unref();
|
|
17198
|
-
});
|
|
17199
|
-
watcher.on("error", () => {
|
|
17200
|
-
watcher.close();
|
|
17201
|
-
if (this.watcher === watcher) this.watcher = void 0;
|
|
17202
|
-
});
|
|
17203
|
-
} catch {
|
|
17204
|
-
}
|
|
17205
|
-
}
|
|
17206
|
-
stop() {
|
|
17207
|
-
clearTimeout(this.notify);
|
|
17208
|
-
this.watcher?.close();
|
|
17209
|
-
this.watcher = void 0;
|
|
17210
|
-
this.server?.closeAllConnections();
|
|
17211
|
-
this.server?.close();
|
|
17212
|
-
this.server = void 0;
|
|
17213
|
-
}
|
|
17214
|
-
};
|
|
17215
|
-
|
|
17216
17325
|
// src/harness/vscode.ts
|
|
17217
17326
|
import Socket3 from "ws";
|
|
17218
|
-
import { homedir as
|
|
17327
|
+
import { homedir as homedir6 } from "node:os";
|
|
17219
17328
|
import { connect as connect2 } from "node:net";
|
|
17220
17329
|
import { promisify as promisify3 } from "node:util";
|
|
17221
17330
|
import { fileURLToPath } from "node:url";
|
|
@@ -17242,7 +17351,7 @@ function roots() {
|
|
|
17242
17351
|
if (override !== void 0)
|
|
17243
17352
|
return override ? [basename2(override) === "User" ? dirname5(override) : override] : [];
|
|
17244
17353
|
if (process.env.DASHUP_LIVE_DISCOVERY === "0") return [];
|
|
17245
|
-
const base = process.platform === "darwin" ? join6(
|
|
17354
|
+
const base = process.platform === "darwin" ? join6(homedir6(), "Library/Application Support") : process.platform === "win32" ? process.env.APPDATA ?? join6(homedir6(), "AppData/Roaming") : process.env.XDG_CONFIG_HOME ?? join6(homedir6(), ".config");
|
|
17246
17355
|
return ["Code", "Code - Insiders"].map((name) => join6(base, name));
|
|
17247
17356
|
}
|
|
17248
17357
|
async function privateFile(path, limit) {
|
|
@@ -17715,7 +17824,7 @@ var VSCode = class {
|
|
|
17715
17824
|
import { join as join7 } from "node:path";
|
|
17716
17825
|
import { promisify as promisify4 } from "node:util";
|
|
17717
17826
|
import { createHash as createHash4 } from "node:crypto";
|
|
17718
|
-
import { execFile as execFile4, spawn as
|
|
17827
|
+
import { execFile as execFile4, spawn as spawn4 } from "node:child_process";
|
|
17719
17828
|
import { stat as stat3, readlink, readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
|
|
17720
17829
|
var run2 = promisify4(execFile4);
|
|
17721
17830
|
var REQUEST_MS3 = 4e3;
|
|
@@ -18183,7 +18292,7 @@ var Server = class {
|
|
|
18183
18292
|
}
|
|
18184
18293
|
};
|
|
18185
18294
|
function serve(cwd) {
|
|
18186
|
-
const child =
|
|
18295
|
+
const child = spawn4("opencode", ["serve", "--hostname", "127.0.0.1", "--port", "0"], {
|
|
18187
18296
|
cwd,
|
|
18188
18297
|
stdio: ["ignore", "pipe", "ignore"],
|
|
18189
18298
|
shell: process.platform === "win32"
|
|
@@ -18304,9 +18413,10 @@ var LiveHarnesses = class {
|
|
|
18304
18413
|
});
|
|
18305
18414
|
return this.refreshing;
|
|
18306
18415
|
}
|
|
18307
|
-
// The agents whose own command runs on this computer.
|
|
18416
|
+
// The agents whose own command runs on this computer, and the models Claude Code offers.
|
|
18308
18417
|
async agents() {
|
|
18309
18418
|
const harnesses = ["claude", "codex", "opencode"];
|
|
18419
|
+
const asked = askModels();
|
|
18310
18420
|
const runs = await Promise.all(
|
|
18311
18421
|
harnesses.map((harness) => promisify5(execFile5)(harness, ["--version"], {
|
|
18312
18422
|
timeout: 1e4,
|
|
@@ -18316,6 +18426,7 @@ var LiveHarnesses = class {
|
|
|
18316
18426
|
() => false
|
|
18317
18427
|
))
|
|
18318
18428
|
);
|
|
18429
|
+
await asked;
|
|
18319
18430
|
return harnesses.filter((_, index) => runs[index]);
|
|
18320
18431
|
}
|
|
18321
18432
|
// Starts a session in `cwd` with its first message, and resolves with the session's native id.
|
|
@@ -18365,7 +18476,7 @@ var LiveHarnesses = class {
|
|
|
18365
18476
|
|
|
18366
18477
|
// src/harness/queue.ts
|
|
18367
18478
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
18368
|
-
import { spawn as
|
|
18479
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
18369
18480
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
18370
18481
|
import { join as join8, dirname as dirname6, basename as basename3 } from "node:path";
|
|
18371
18482
|
import { statSync as statSync2, existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync4, readlinkSync, realpathSync as realpathSync2 } from "node:fs";
|
|
@@ -18562,7 +18673,7 @@ var Writer = class {
|
|
|
18562
18673
|
tail = "";
|
|
18563
18674
|
pending = /* @__PURE__ */ new Map();
|
|
18564
18675
|
constructor(owner) {
|
|
18565
|
-
this.process =
|
|
18676
|
+
this.process = spawn5(owner.executable, ["app-server", "--listen", "stdio://"], {
|
|
18566
18677
|
cwd: owner.cwd,
|
|
18567
18678
|
env: { ...process.env, CODEX_HOME: owner.home },
|
|
18568
18679
|
stdio: ["pipe", "pipe", "ignore"]
|
|
@@ -18739,6 +18850,11 @@ var Command = z2.object({
|
|
|
18739
18850
|
}).strict(),
|
|
18740
18851
|
z2.object({ kind: z2.literal("thread.queue"), text: z2.string().min(1).max(1e5) }).strict(),
|
|
18741
18852
|
z2.object({ kind: z2.literal("thread.stop") }).strict(),
|
|
18853
|
+
z2.object({
|
|
18854
|
+
kind: z2.literal("thread.configure"),
|
|
18855
|
+
option: z2.string().min(1).max(100),
|
|
18856
|
+
value: z2.union([z2.string().min(1).max(200), z2.boolean()])
|
|
18857
|
+
}).strict(),
|
|
18742
18858
|
z2.object({ kind: z2.literal("approval.respond"), approvalId: z2.string(), choice: z2.string() }).strict(),
|
|
18743
18859
|
z2.object({ kind: z2.literal("attachment.read"), id: z2.string().regex(/^[a-f0-9]{64}$/) }).strict(),
|
|
18744
18860
|
z2.object({
|
|
@@ -19078,13 +19194,15 @@ var Connector = class {
|
|
|
19078
19194
|
const detached = {
|
|
19079
19195
|
...snapshot.thread,
|
|
19080
19196
|
status: "unknown",
|
|
19197
|
+
options: void 0,
|
|
19081
19198
|
capabilities: {
|
|
19082
19199
|
...snapshot.thread.capabilities,
|
|
19083
19200
|
send: false,
|
|
19084
19201
|
approve: false,
|
|
19085
19202
|
// A separate queue owner can remain valid after its live socket detaches.
|
|
19086
19203
|
queue: thread?.capabilities.queue ?? false,
|
|
19087
|
-
stop: false
|
|
19204
|
+
stop: false,
|
|
19205
|
+
configure: false
|
|
19088
19206
|
}
|
|
19089
19207
|
};
|
|
19090
19208
|
if (thread) Object.assign(thread, detached);
|
|
@@ -19254,7 +19372,9 @@ var Connector = class {
|
|
|
19254
19372
|
async execute(pair, content, envelope) {
|
|
19255
19373
|
let queue = void 0;
|
|
19256
19374
|
try {
|
|
19257
|
-
if (["approval.respond", "thread.stop", "session.start"].includes(
|
|
19375
|
+
if (["approval.respond", "thread.stop", "thread.configure", "session.start"].includes(
|
|
19376
|
+
content.action.kind
|
|
19377
|
+
) && Math.abs(Date.now() - Date.parse(envelope.sentAt)) > EPHEMERAL_TTL_MS)
|
|
19258
19378
|
throw new Error("Command expired. Try again while this computer is online.");
|
|
19259
19379
|
const outcome = await this.command(pair, content);
|
|
19260
19380
|
delete this.config.unconfirmed[content.requestId];
|
|
@@ -19461,6 +19581,16 @@ var Connector = class {
|
|
|
19461
19581
|
);
|
|
19462
19582
|
return commandAcp(agent, command2.requestId, { kind: "cancel" });
|
|
19463
19583
|
}
|
|
19584
|
+
case "thread.configure": {
|
|
19585
|
+
const { option: option2, value: value2 } = command2.action;
|
|
19586
|
+
const native = thread && this.discovery.locations.get(thread.id)?.live;
|
|
19587
|
+
const known = thread?.options?.find((item) => item.id === option2);
|
|
19588
|
+
if (!native?.configure || !known)
|
|
19589
|
+
throw new Error("This session can only be changed on your computer.");
|
|
19590
|
+
if (typeof value2 !== typeof known.value || known.choices && !known.choices.some((choice) => choice.value === value2))
|
|
19591
|
+
throw new Error(`${known.name} can't be set to that.`);
|
|
19592
|
+
return native.configure(command2.requestId, option2, value2);
|
|
19593
|
+
}
|
|
19464
19594
|
case "thread.queue": {
|
|
19465
19595
|
if (!thread) throw new Error("Thread not found");
|
|
19466
19596
|
this.nativeQueue.refresh();
|
|
@@ -19543,10 +19673,10 @@ var Connector = class {
|
|
|
19543
19673
|
|
|
19544
19674
|
// src/install.ts
|
|
19545
19675
|
import { existsSync as existsSync5 } from "node:fs";
|
|
19546
|
-
import { homedir as
|
|
19676
|
+
import { homedir as homedir7, userInfo } from "node:os";
|
|
19547
19677
|
import { promisify as promisify6 } from "node:util";
|
|
19548
19678
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19549
|
-
import { execFile as execFile6, spawn as
|
|
19679
|
+
import { execFile as execFile6, spawn as spawn6 } from "node:child_process";
|
|
19550
19680
|
import { cp, mkdir as mkdir3, rm as rm2, writeFile } from "node:fs/promises";
|
|
19551
19681
|
import { dirname as dirname7, join as join10, relative, sep } from "node:path";
|
|
19552
19682
|
var run3 = promisify6(execFile6);
|
|
@@ -19555,10 +19685,10 @@ var NODE_FLAGS = ["--disable-warning=ExperimentalWarning"];
|
|
|
19555
19685
|
var systemctl = (...args2) => run3("systemctl", ["--user", ...args2], {
|
|
19556
19686
|
env: { ...process.env, XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR ?? `/run/user/${userInfo().uid}` }
|
|
19557
19687
|
});
|
|
19558
|
-
var serviceFile = () => process.platform === "darwin" ? join10(
|
|
19559
|
-
process.env.APPDATA ?? join10(
|
|
19688
|
+
var serviceFile = () => process.platform === "darwin" ? join10(homedir7(), "Library/LaunchAgents/com.dashup.connector.plist") : process.platform === "win32" ? join10(
|
|
19689
|
+
process.env.APPDATA ?? join10(homedir7(), "AppData/Roaming"),
|
|
19560
19690
|
"Microsoft/Windows/Start Menu/Programs/Startup/Dashup.vbs"
|
|
19561
|
-
) : join10(
|
|
19691
|
+
) : join10(homedir7(), ".config/systemd/user/dashup.service");
|
|
19562
19692
|
var startupInstalled = () => existsSync5(serviceFile());
|
|
19563
19693
|
function service(platform, node, entry, config2) {
|
|
19564
19694
|
const command2 = [node, ...NODE_FLAGS, entry];
|
|
@@ -19627,7 +19757,7 @@ async function start(path) {
|
|
|
19627
19757
|
});
|
|
19628
19758
|
await run3("launchctl", ["load", path]);
|
|
19629
19759
|
} else if (process.platform === "win32") {
|
|
19630
|
-
|
|
19760
|
+
spawn6("wscript.exe", [path], { detached: true, stdio: "ignore" }).unref();
|
|
19631
19761
|
} else {
|
|
19632
19762
|
const note = process.env.DISPLAY || process.env.WAYLAND_DISPLAY ? void 0 : await run3("loginctl", ["enable-linger"]).then(
|
|
19633
19763
|
() => void 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dashup/connector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dashup": "dist/index.js"
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"tsup": "^8.5.1",
|
|
31
31
|
"tsx": "^4.22.4",
|
|
32
32
|
"typescript": "^5.9.3",
|
|
33
|
-
"@dashup/models": "0.1.0",
|
|
34
33
|
"@dashup/protocol": "0.1.0",
|
|
35
34
|
"@dashup/typescript-config": "0.0.0",
|
|
36
|
-
"@
|
|
37
|
-
"@parcae/sdk": "0.8.1"
|
|
35
|
+
"@dashup/models": "0.1.0",
|
|
36
|
+
"@parcae/sdk": "0.8.1",
|
|
37
|
+
"@parcae/lynx": "0.1.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"dashup": "tsx src/index.ts",
|