@lawaty/lacode 1.0.2 → 1.0.3

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/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ All notable changes to LaCode are recorded here. The format follows
7
7
  Versions come from git release commits; the npm version is bumped in the same
8
8
  commit as the changelog entry it describes.
9
9
 
10
+ ## [1.0.3] — one notification even when the plugin is loaded twice
11
+
12
+ ### Fixed
13
+
14
+ - **A question or permission popup is raised once when two LaCode instances share
15
+ a process.** opencode auto-discovers every file under
16
+ `~/.config/opencode/plugins/`, so a host that also lists `@lawaty/lacode` in
17
+ `opencode.jsonc` loaded two copies. `done` was already deduplicated by the
18
+ completion marker, but the notification claim treated any claim holding our
19
+ own pid as ours — indistinguishable between two instances in one process. The
20
+ claim now carries a per-instance token, so the second instance loses while the
21
+ same instance may still raise a new prompt.
22
+
23
+ ### Changed
24
+
25
+ - **Publishing syncs the published version to every host.** `postpublish` runs
26
+ `scripts/sync-hosts.mjs`: it installs `@lawaty/lacode@<version>`, normalises
27
+ each host's config to a single LaCode entry (preserving its options), disables
28
+ stale auto-discovered shims, and warns when a host cannot run the helper.
29
+
10
30
  ## [1.0.2] — the permission chime plays when the popup appears
11
31
 
12
32
  ### Fixed
package/dist/index.js CHANGED
@@ -3662,6 +3662,7 @@ function scrubSlotNames(text) {
3662
3662
 
3663
3663
  // src/plugins/notify.ts
3664
3664
  import { spawn as spawn2 } from "node:child_process";
3665
+ import { randomUUID as randomUUID2 } from "node:crypto";
3665
3666
  import { existsSync as existsSync3 } from "node:fs";
3666
3667
  import * as path3 from "node:path";
3667
3668
 
@@ -3857,7 +3858,7 @@ var isPidAlive2 = (pid) => {
3857
3858
  }
3858
3859
  };
3859
3860
  var claimFile = (sessionID, kind) => join7(notificationStateDir(), `claim.${safeCorrelationKey(`${sessionID}.${kind}`)}`);
3860
- var claimNotification = (sessionID, kind) => {
3861
+ var claimNotification = (sessionID, kind, instanceId = "") => {
3861
3862
  const path4 = claimFile(sessionID, kind);
3862
3863
  try {
3863
3864
  mkdirSync5(notificationStateDir(), { recursive: true });
@@ -3868,7 +3869,7 @@ var claimNotification = (sessionID, kind) => {
3868
3869
  try {
3869
3870
  const fd = openSync2(path4, "wx");
3870
3871
  try {
3871
- writeSync2(fd, String(process.pid));
3872
+ writeSync2(fd, `${process.pid} ${instanceId} ${kind}`);
3872
3873
  } finally {
3873
3874
  closeSync2(fd);
3874
3875
  }
@@ -3888,13 +3889,21 @@ var claimNotification = (sessionID, kind) => {
3888
3889
  } catch (error) {
3889
3890
  return error.code === "ENOENT" ? "gone" : "unknown";
3890
3891
  }
3892
+ const fields = raw.split(/\s+/).filter((entry) => entry.length > 0);
3891
3893
  let holder;
3892
3894
  const empty = raw === "";
3893
3895
  if (!empty) {
3894
- const parsed = Number.parseInt(raw, 10);
3896
+ const parsed = Number.parseInt(fields[0] ?? "", 10);
3895
3897
  holder = Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
3896
3898
  }
3897
- if (holder === process.pid) return "ours";
3899
+ if (holder === process.pid) {
3900
+ const hasToken = fields.length >= 3;
3901
+ const fileToken = hasToken ? fields[1] ?? "" : "";
3902
+ if (hasToken && instanceId !== "" && fileToken !== instanceId) {
3903
+ return "held";
3904
+ }
3905
+ return "ours";
3906
+ }
3898
3907
  let age = Number.POSITIVE_INFINITY;
3899
3908
  try {
3900
3909
  age = Date.now() - statSync5(path4).mtimeMs;
@@ -4050,6 +4059,7 @@ var notifyHooks = (async ({ client, directory }, options, deps = {}) => {
4050
4059
  const soundEnv = (process.env[SOUND_ENV] ?? "").trim().toLowerCase();
4051
4060
  const soundOn = !SOUND_OFF_VALUES.includes(soundEnv) && opts.sound !== false;
4052
4061
  const notifier = new Notifier(COOLDOWN_MS2);
4062
+ const instanceId = randomUUID2();
4053
4063
  const active = /* @__PURE__ */ new Set();
4054
4064
  const titles = /* @__PURE__ */ new Map();
4055
4065
  const pending = /* @__PURE__ */ new Set();
@@ -4345,7 +4355,7 @@ var notifyHooks = (async ({ client, directory }, options, deps = {}) => {
4345
4355
  void log("debug", "suppressed: completion already announced", { sessionID });
4346
4356
  return;
4347
4357
  }
4348
- } else if (!claimNotification(sessionID, kind)) {
4358
+ } else if (!claimNotification(sessionID, kind, instanceId)) {
4349
4359
  void log("debug", "suppressed: another process is raising this", { sessionID, kind });
4350
4360
  return;
4351
4361
  } else if (outcome === "error") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawaty/lacode",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Ultimate opencode setup for Go users — cost-controlled pooled subagents, hang recovery, and notifications for opencode.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -52,6 +52,7 @@
52
52
  "scripts": {
53
53
  "build": "node scripts/build.mjs",
54
54
  "prepack": "npm run build",
55
+ "postpublish": "node scripts/sync-hosts.mjs",
55
56
  "test": "node --test tests/",
56
57
  "benchmark": "node tests/benchmark/run.mjs"
57
58
  },