@sessionbus/dsh 0.1.0-pre.10 → 0.1.0-pre.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.
Files changed (3) hide show
  1. package/README.md +6 -0
  2. package/package.json +1 -1
  3. package/plugin.cjs +43 -10
package/README.md CHANGED
@@ -62,6 +62,12 @@ when given a launch token. Install the selected launcher alongside `dsh` at the
62
62
  host level and put their shared `node_modules/.bin` on the daemon service's
63
63
  `PATH`; the `dashi` launcher resolves its child `dsh` by name.
64
64
 
65
+ Without an explicit socket, peers use `$XDG_RUNTIME_DIR/sessionbus/presence.sock`
66
+ or `/tmp/sessionbus-<uid>/presence.sock`, matching the daemon's discovery rule.
67
+ Set `SESSIONBUS_DSH_TRACE=1` to write one-line mode, readiness, root-lifecycle,
68
+ publication-gate, and socket-connection diagnostics to stderr; tracing is off
69
+ by default.
70
+
65
71
  Uninstall without invoking DSH by running the installed bin from the profile:
66
72
 
67
73
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sessionbus/dsh",
3
- "version": "0.1.0-pre.10",
3
+ "version": "0.1.0-pre.12",
4
4
  "type": "commonjs",
5
5
  "main": "plugin.cjs",
6
6
  "exports": "./plugin.cjs",
package/plugin.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- const os = require("node:os");
3
+ const net = require("node:net");
4
4
  const path = require("node:path");
5
5
  const kit = require("@sessionbus/kit");
6
6
  const version = require("./package.json").version;
@@ -66,10 +66,13 @@ function readConfiguration(ctx, config = {}, ambient = process.env) {
66
66
  if (config.mode !== undefined && config.mode !== mode) throw new Error(`mode ${config.mode} conflicts with launch environment`);
67
67
  let groups = mode === "lane" ? [] : Object.hasOwn(config, "groups") ? config.groups : JSON.parse(value("SESSIONBUS_GROUPS") || "[]");
68
68
  if (!Array.isArray(groups) || groups.some((group) => !text(group)) || new Set(groups).size !== groups.length) throw new Error("groups are invalid");
69
- const socket = Object.hasOwn(config, "socket") ? config.socket : value("SESSIONBUS_SOCKET") || path.join(value("XDG_STATE_HOME") || path.join(value("HOME") || os.homedir(), ".local/state"), "sessionbus/run/presence.sock");
69
+ const explicitSocket = Object.hasOwn(config, "socket") ? config.socket : value("SESSIONBUS_SOCKET");
70
+ const socket = explicitSocket ? path.resolve(explicitSocket) : value("XDG_RUNTIME_DIR")
71
+ ? path.resolve(value("XDG_RUNTIME_DIR"), "sessionbus/presence.sock")
72
+ : path.join("/tmp", `sessionbus-${process.getuid()}`, "presence.sock");
70
73
  const localKey = Object.hasOwn(config, "local_key") ? config.local_key : value("SESSIONBUS_LOCAL_KEY");
71
74
  if (!text(socket) || localKey !== undefined && !text(localKey)) throw new Error("connection settings are invalid");
72
- return { settings: { mode, product: config.product, groups: [...groups], socket, localKey }, token };
75
+ return { settings: { mode, product: config.product, groups: [...groups], socket, localKey }, token, trace: value("SESSIONBUS_DSH_TRACE") === "1" };
73
76
  }
74
77
 
75
78
  function captureContext(ctx) {
@@ -285,26 +288,54 @@ function createRuntime(ctx, config, dependencies, prepared) {
285
288
  let launchToken = configured.token;
286
289
  const native = new NativeSession(ctx, dependencies.createUserMessage);
287
290
  const peers = new Map();
291
+ const publicationErrors = new WeakSet();
288
292
  let worker;
289
293
  let workerExit = Promise.resolve();
290
294
  let ready = false;
291
295
  let warned = false;
292
296
  const warn = (error) => { if (active() && !warned) { warned = true; dependencies.stderr(`sessionbus: ${clean(error)}\n`); } };
297
+ const trace = (message) => { if (configured.trace && active()) dependencies.stderr(`sessionbus trace: ${message}\n`); };
293
298
  const root = (agent) => ctx.agents.roots().includes(agent);
299
+ trace(`mode=${values.mode} ready=false socket=${values.socket}`);
300
+ const publicationError = (agent, error, record) => {
301
+ if (record && peers.get(agent) === record) {
302
+ record.peer?.shutdown();
303
+ peers.delete(agent);
304
+ // Leave it unpublished; a later title change may naturally call present() again.
305
+ }
306
+ if (active() && !publicationErrors.has(agent)) {
307
+ publicationErrors.add(agent);
308
+ dependencies.stderr(`sessionbus: ${clean(error)}\n`);
309
+ }
310
+ };
294
311
  const present = (agent) => {
295
- if (values.mode !== "peer" || !ready || !root(agent) || peers.has(agent)) return;
312
+ const isRoot = root(agent);
313
+ const reason = values.mode !== "peer" ? "not-peer" : !ready ? "not-ready" : !isRoot ? "not-root" : peers.has(agent) ? "published" : "publish";
314
+ trace(`present id=${agent?.session?.id || agent?.id || "unknown"} root=${isRoot} reason=${reason}`);
315
+ if (reason !== "publish") return;
296
316
  try {
297
317
  const current = identity(ctx, agent, values.product, values.groups);
298
- if (!current) return;
299
- const peer = dependencies.connectPeer(current, (cancel, request) => native.deliver(cancel, request, agent), connectionEnvironment(values));
300
- peers.set(agent, { peer, identity: current, rehello: Promise.resolve() });
301
- } catch (error) { warn(error); }
318
+ if (!current) { trace(`present id=${agent?.id || "unknown"} root=${isRoot} reason=no-session-id`); return; }
319
+ const record = { peer: undefined, identity: current, rehello: Promise.resolve() };
320
+ const peer = dependencies.connectPeer(current, (cancel, request) => native.deliver(cancel, request, agent), connectionEnvironment(values), {
321
+ connect: (socket) => {
322
+ trace(`connect id=${current.session_id} socket=${socket}`);
323
+ const stream = net.createConnection(socket);
324
+ stream.once("connect", () => trace(`connect id=${current.session_id} state=connected`));
325
+ stream.once("error", (error) => publicationError(agent, error, record));
326
+ return stream;
327
+ },
328
+ });
329
+ record.peer = peer;
330
+ peers.set(agent, record);
331
+ void peer.closed?.then(() => { if (peer.error) publicationError(agent, peer.error, record); });
332
+ } catch (error) { publicationError(agent, error); }
302
333
  };
303
334
  const forget = (agent) => { peers.get(agent)?.peer.shutdown(); peers.delete(agent); };
304
335
  let removeCreated = () => {}, removeDisposed = () => {}, removeTitle = () => {};
305
336
  if (values.mode === "peer") {
306
- removeCreated = ctx.on("agent/created", ({ agent }) => present(agent));
307
- removeDisposed = ctx.on("agent/disposed", ({ agent }) => forget(agent));
337
+ removeCreated = ctx.on("agent/created", ({ agent }) => { trace(`agent/created id=${agent?.session?.id || agent?.id || "unknown"} scope=global`); present(agent); }, { global: true });
338
+ removeDisposed = ctx.on("agent/disposed", ({ agent }) => { trace(`agent/disposed id=${agent?.session?.id || agent?.id || "unknown"} scope=global`); forget(agent); }, { global: true });
308
339
  removeTitle = ctx.on("session/event", (session, event) => {
309
340
  if (event.type !== "session/title") return;
310
341
  const agent = ctx.agents.get(session.id);
@@ -348,6 +379,7 @@ function createRuntime(ctx, config, dependencies, prepared) {
348
379
  });
349
380
  const start = () => {
350
381
  ready = true;
382
+ trace(`mode=${values.mode} ready=true`);
351
383
  if (values.mode === "lane") {
352
384
  const environment = connectionEnvironment(values, launchToken);
353
385
  launchToken = undefined;
@@ -370,6 +402,7 @@ function createRuntime(ctx, config, dependencies, prepared) {
370
402
  };
371
403
  const removeReady = ctx.appReady.onReady(start);
372
404
  const close = () => {
405
+ trace(`mode=${values.mode} ready=false reason=close`);
373
406
  removeReady(); removeCreated(); removeDisposed(); removeTitle(); removeCommand(); removeGrant(); removeTool();
374
407
  for (const agent of peers.keys()) forget(agent);
375
408
  worker?.shutdown(); native.removeEvents();