@dashup/connector 0.1.14 → 0.1.15

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +44 -26
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -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
- 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.
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 the session's own Claude Code binary offers hosts in its initialize answer, or `claude`'s where the system does not name the binary. 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
51
 
52
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.
53
53
 
package/dist/index.js CHANGED
@@ -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.14";
4051
+ var version = "0.1.15";
4052
4052
 
4053
4053
  // src/pair.ts
4054
4054
  var Invitation = z.object({
@@ -13246,22 +13246,22 @@ var FrontendAdapter = class {
13246
13246
  if (options.subscribe === false) requestData.__subscribe = false;
13247
13247
  const result = await this.transport.get(path, requestData);
13248
13248
  const items = result?.[(0, import_pluralize.default)(modelClass.type)] ?? result?.[modelClass.type + "s"] ?? result?.items ?? [];
13249
- const models2 = items.map(
13249
+ const models = items.map(
13250
13250
  (row) => modelClass.hydrate(this, row)
13251
13251
  );
13252
13252
  if (result?.__queryHash) {
13253
- Object.defineProperty(models2, "__queryHash", {
13253
+ Object.defineProperty(models, "__queryHash", {
13254
13254
  value: result.__queryHash,
13255
13255
  enumerable: false
13256
13256
  });
13257
13257
  }
13258
13258
  if (typeof result?.totalCount === "number") {
13259
- Object.defineProperty(models2, "__totalCount", {
13259
+ Object.defineProperty(models, "__totalCount", {
13260
13260
  value: result.totalCount,
13261
13261
  enumerable: false
13262
13262
  });
13263
13263
  }
13264
- return models2;
13264
+ return models;
13265
13265
  };
13266
13266
  chain.first = async () => {
13267
13267
  const all = await chain.find();
@@ -16821,7 +16821,7 @@ import { dirname as dirname4, join as join5 } from "node:path";
16821
16821
  import { spawn as spawn2 } from "node:child_process";
16822
16822
  import { createInterface as createInterface2 } from "node:readline";
16823
16823
  import { watch as watch2 } from "node:fs";
16824
- import { mkdir, readdir as readdir2, readFile as readFile2, rm } from "node:fs/promises";
16824
+ import { mkdir, readdir as readdir2, readFile as readFile2, readlink, rm } from "node:fs/promises";
16825
16825
  import { createServer as createServer2 } from "node:http";
16826
16826
  var MODELS_MS = 3e4;
16827
16827
  var COMMANDS = {
@@ -16829,32 +16829,48 @@ var COMMANDS = {
16829
16829
  fast: (value2) => `/fast ${value2 ? "on" : "off"}`,
16830
16830
  ultracode: (value2) => `/effort ultracode ${value2 ? "on" : "off"}`
16831
16831
  };
16832
- var models = [];
16833
- function askModels() {
16832
+ var offered = /* @__PURE__ */ new Map();
16833
+ var asking = /* @__PURE__ */ new Set();
16834
+ var forgetModels = () => offered.clear();
16835
+ function claudeModels(changed, command2 = "claude", version2 = "") {
16836
+ const key = `${command2}\0${version2}`;
16837
+ if (!offered.has(key) && !asking.has(key)) {
16838
+ asking.add(key);
16839
+ void listModels(command2).then((models) => {
16840
+ asking.delete(key);
16841
+ if (!models.length) return;
16842
+ offered.set(key, models);
16843
+ changed();
16844
+ });
16845
+ }
16846
+ return offered.get(key) ?? [];
16847
+ }
16848
+ function listModels(command2) {
16834
16849
  return new Promise((resolve3) => {
16835
16850
  const child = spawn2(
16836
- "claude",
16851
+ command2,
16837
16852
  ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"],
16838
16853
  { cwd: homedir5(), stdio: "pipe", shell: process.platform === "win32" }
16839
16854
  );
16840
16855
  const timer = setTimeout(() => child.kill(), MODELS_MS);
16841
16856
  child.stdin.on("error", () => {
16842
16857
  });
16843
- child.once("error", () => resolve3());
16858
+ child.once("error", () => resolve3([]));
16844
16859
  child.once("exit", () => {
16845
16860
  clearTimeout(timer);
16846
- resolve3();
16861
+ resolve3([]);
16847
16862
  });
16848
16863
  createInterface2({ input: child.stdout }).on("line", (line) => {
16849
16864
  try {
16850
16865
  const message = JSON.parse(line);
16851
16866
  if (message?.type !== "control_response" || message.response?.request_id !== "models")
16852
16867
  return;
16853
- const offered = message.response.response?.models;
16854
- if (Array.isArray(offered))
16855
- models = offered.filter(
16868
+ const models = message.response.response?.models;
16869
+ resolve3(
16870
+ (Array.isArray(models) ? models : []).filter(
16856
16871
  (model) => typeof model?.value === "string" && typeof model.displayName === "string"
16857
- );
16872
+ )
16873
+ );
16858
16874
  child.stdin.end();
16859
16875
  } catch {
16860
16876
  }
@@ -16865,7 +16881,7 @@ function askModels() {
16865
16881
  );
16866
16882
  });
16867
16883
  }
16868
- function claudeOptions(recorded) {
16884
+ function claudeOptions(models, recorded) {
16869
16885
  const ran = typeof recorded.model === "string" ? recorded.model : "";
16870
16886
  const current = models.find((model) => model.value !== "default" && model.resolvedModel === ran) ?? models.find((model) => model.value === ran);
16871
16887
  const choices = models.map((model) => ({ value: model.value, name: model.displayName }));
@@ -16896,7 +16912,8 @@ async function registrations() {
16896
16912
  const value2 = JSON.parse(await readFile2(join5(directory2, name), "utf8"));
16897
16913
  if (typeof value2.sessionId !== "string" || typeof value2.cwd !== "string") return [];
16898
16914
  process.kill(value2.pid, 0);
16899
- return [value2];
16915
+ const binary2 = await readlink(`/proc/${value2.pid}/exe`).catch(() => "");
16916
+ return [{ ...value2, command: /[\\/]claude(?:\.exe)?$/.test(binary2) ? binary2 : "claude" }];
16900
16917
  } catch {
16901
16918
  return [];
16902
16919
  }
@@ -16920,6 +16937,7 @@ var Claude = class {
16920
16937
  this.watch();
16921
16938
  return (await registrations()).map((session) => {
16922
16939
  const plugin = this.streams.has(session.sessionId);
16940
+ const models = claudeModels(this.changed, session.command, session.version);
16923
16941
  return {
16924
16942
  id: session.sessionId,
16925
16943
  harness: "claude",
@@ -16931,7 +16949,7 @@ var Claude = class {
16931
16949
  send: plugin ? (requestId, text3, now) => this.once(requestId, () => this.deliver(session.sessionId, now ? { text: text3, now } : { text: text3 })) : void 0,
16932
16950
  stop: plugin ? (requestId) => this.once(requestId, () => this.deliver(session.sessionId, { now: true })) : void 0,
16933
16951
  now: plugin,
16934
- options: claudeOptions,
16952
+ options: (recorded) => claudeOptions(models, recorded),
16935
16953
  configure: plugin ? (requestId, option2, value2) => this.once(requestId, () => this.deliver(session.sessionId, { text: COMMANDS[option2](value2) })) : void 0
16936
16954
  };
16937
16955
  });
@@ -17229,7 +17247,7 @@ var Session = class {
17229
17247
  this.changed();
17230
17248
  return init;
17231
17249
  }
17232
- live() {
17250
+ live(models) {
17233
17251
  return {
17234
17252
  id: this.id,
17235
17253
  cwd: this.cwd,
@@ -17242,7 +17260,7 @@ var Session = class {
17242
17260
  approvals: [...this.approvals.values()].map((pending) => pending.approval),
17243
17261
  send: (requestId, text3, now) => this.once(requestId, () => this.say(text3, now)),
17244
17262
  now: true,
17245
- options: claudeOptions,
17263
+ options: (recorded) => claudeOptions(models, recorded),
17246
17264
  configure: (requestId, option2, value2) => this.once(requestId, async () => {
17247
17265
  this.chosen[option2] = value2;
17248
17266
  this.apply(option2, value2);
@@ -17314,7 +17332,8 @@ var Headless = class {
17314
17332
  }
17315
17333
  }
17316
17334
  async refresh() {
17317
- return [...this.sessions.values()].map((session) => session.live());
17335
+ const models = claudeModels(this.changed);
17336
+ return [...this.sessions.values()].map((session) => session.live(models));
17318
17337
  }
17319
17338
  stop() {
17320
17339
  clearTimeout(this.notify);
@@ -17825,7 +17844,7 @@ import { join as join7 } from "node:path";
17825
17844
  import { promisify as promisify4 } from "node:util";
17826
17845
  import { createHash as createHash4 } from "node:crypto";
17827
17846
  import { execFile as execFile4, spawn as spawn4 } from "node:child_process";
17828
- import { stat as stat3, readlink, readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
17847
+ import { stat as stat3, readlink as readlink2, readdir as readdir4, readFile as readFile4 } from "node:fs/promises";
17829
17848
  var run2 = promisify4(execFile4);
17830
17849
  var REQUEST_MS3 = 4e3;
17831
17850
  var MAX_BYTES3 = 8 * 1024 * 1024;
@@ -17880,7 +17899,7 @@ async function linuxEndpoints() {
17880
17899
  })
17881
17900
  );
17882
17901
  for (const fd of (await readdir4(join7(directory2, "fd"))).slice(0, 1024)) {
17883
- const target = await readlink(join7(directory2, "fd", fd)).catch(() => "");
17902
+ const target = await readlink2(join7(directory2, "fd", fd)).catch(() => "");
17884
17903
  const inode = /^socket:\[(\d+)\]$/.exec(target)?.[1];
17885
17904
  const url2 = inode ? listeners.get(inode) : void 0;
17886
17905
  if (url2) result.push({ url: url2, authorization: authorization(environment) });
@@ -18413,10 +18432,10 @@ var LiveHarnesses = class {
18413
18432
  });
18414
18433
  return this.refreshing;
18415
18434
  }
18416
- // The agents whose own command runs on this computer, and the models Claude Code offers.
18435
+ // The agents whose own command runs on this computer. Claude Code is asked its models again.
18417
18436
  async agents() {
18418
18437
  const harnesses = ["claude", "codex", "opencode"];
18419
- const asked = askModels();
18438
+ forgetModels();
18420
18439
  const runs = await Promise.all(
18421
18440
  harnesses.map((harness) => promisify5(execFile5)(harness, ["--version"], {
18422
18441
  timeout: 1e4,
@@ -18426,7 +18445,6 @@ var LiveHarnesses = class {
18426
18445
  () => false
18427
18446
  ))
18428
18447
  );
18429
- await asked;
18430
18448
  return harnesses.filter((_, index) => runs[index]);
18431
18449
  }
18432
18450
  // Starts a session in `cwd` with its first message, and resolves with the session's native id.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dashup/connector",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "dashup": "dist/index.js"
@@ -31,10 +31,10 @@
31
31
  "tsx": "^4.22.4",
32
32
  "typescript": "^5.9.3",
33
33
  "@dashup/protocol": "0.1.0",
34
- "@dashup/typescript-config": "0.0.0",
35
- "@dashup/models": "0.1.0",
34
+ "@parcae/lynx": "0.1.0",
36
35
  "@parcae/sdk": "0.8.1",
37
- "@parcae/lynx": "0.1.0"
36
+ "@dashup/typescript-config": "0.0.0",
37
+ "@dashup/models": "0.1.0"
38
38
  },
39
39
  "scripts": {
40
40
  "dashup": "tsx src/index.ts",