@khalilgharbaoui/opencode-claude-code-plugin 0.62.0 → 0.62.1

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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/claude-code-language-model.ts
2
- import { join as join19 } from "path";
2
+ import { join as join20 } from "path";
3
3
 
4
4
  // src/ids.ts
5
5
  import { randomInt } from "crypto";
@@ -7856,6 +7856,71 @@ async function requestSideQuestion(activeProcess, question, options) {
7856
7856
  });
7857
7857
  }
7858
7858
 
7859
+ // src/session-event-relay.ts
7860
+ var RELAY_KEY = /* @__PURE__ */ Symbol.for("@khalilgharbaoui/opencode-claude-code-plugin/session-event-relay");
7861
+ function relayState() {
7862
+ const holder = globalThis;
7863
+ let state = holder[RELAY_KEY];
7864
+ if (!state) {
7865
+ state = { members: /* @__PURE__ */ new Set(), followers: 0 };
7866
+ holder[RELAY_KEY] = state;
7867
+ }
7868
+ return state;
7869
+ }
7870
+ function safely(action) {
7871
+ try {
7872
+ action();
7873
+ } catch {
7874
+ }
7875
+ }
7876
+ var ownMember;
7877
+ var ownFollowers = 0;
7878
+ var ownServed = false;
7879
+ function join10() {
7880
+ if (!ownMember) return;
7881
+ const state = relayState();
7882
+ if (state.members.has(ownMember)) return;
7883
+ state.members.add(ownMember);
7884
+ if (state.followers > 0) safely(() => ownMember?.feedOpened());
7885
+ }
7886
+ function leave() {
7887
+ if (!ownMember) return;
7888
+ if (relayState().members.delete(ownMember)) safely(() => ownMember?.feedClosed());
7889
+ }
7890
+ function defineOwnRelayMember(member) {
7891
+ ownMember = member;
7892
+ if (ownServed) join10();
7893
+ }
7894
+ function noteRelayMemberServing() {
7895
+ ownServed = true;
7896
+ join10();
7897
+ }
7898
+ function openRelayFollower() {
7899
+ const state = relayState();
7900
+ join10();
7901
+ ownFollowers += 1;
7902
+ state.followers += 1;
7903
+ if (state.followers === 1) {
7904
+ for (const member of state.members) safely(() => member.feedOpened());
7905
+ }
7906
+ let closed = false;
7907
+ return () => {
7908
+ if (closed) return;
7909
+ closed = true;
7910
+ ownFollowers = Math.max(0, ownFollowers - 1);
7911
+ state.followers = Math.max(0, state.followers - 1);
7912
+ if (state.followers === 0) {
7913
+ for (const member of state.members) safely(() => member.feedClosed());
7914
+ }
7915
+ if (ownFollowers === 0 && !ownServed) leave();
7916
+ };
7917
+ }
7918
+ function relaySessionEvent(event) {
7919
+ const members = relayState().members;
7920
+ if (ownMember && !members.has(ownMember)) safely(() => ownMember?.handle(event));
7921
+ for (const member of [...members]) safely(() => member.handle(event));
7922
+ }
7923
+
7859
7924
  // src/session-manager.ts
7860
7925
  function findActiveProcessBySessionId(sessionID) {
7861
7926
  let found;
@@ -8082,6 +8147,7 @@ function processBelongsToAnotherAccount(active, cliPath, configDir) {
8082
8147
  function setActiveProcess(key, ap) {
8083
8148
  cancelIdleProcessEviction(key);
8084
8149
  activeProcesses.set(key, ap);
8150
+ noteRelayMemberServing();
8085
8151
  }
8086
8152
  function noteInteractiveProcessExit(sessionKey2, ap, code) {
8087
8153
  if (activeProcesses.get(sessionKey2) !== ap) return;
@@ -8376,8 +8442,7 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
8376
8442
  settleTurn(ap);
8377
8443
  lineEmitter.emit("close");
8378
8444
  });
8379
- cancelIdleProcessEviction(sessionKey2);
8380
- activeProcesses.set(sessionKey2, ap);
8445
+ setActiveProcess(sessionKey2, ap);
8381
8446
  proc.on("error", (err) => {
8382
8447
  log.error("claude process error", { sessionKey: sessionKey2, error: err.message });
8383
8448
  });
@@ -10115,20 +10180,20 @@ async function fetchPlanUsage(cliPath, options = {}) {
10115
10180
  // src/turn-history.ts
10116
10181
  import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync6, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
10117
10182
  import { homedir as homedir11 } from "os";
10118
- import { join as join11 } from "path";
10183
+ import { join as join12 } from "path";
10119
10184
 
10120
10185
  // src/turn-progress-store.ts
10121
10186
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync6, renameSync as renameSync5, statSync as statSync5, writeFileSync as writeFileSync6 } from "fs";
10122
10187
  import { homedir as homedir10 } from "os";
10123
- import { join as join10 } from "path";
10188
+ import { join as join11 } from "path";
10124
10189
  var MAX_TURN_PROGRESS = 64;
10125
10190
  var TURN_PROGRESS_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
10126
10191
  var TAG = /^[0-9a-f]{8}$/;
10127
10192
  var pathOverride4 = null;
10128
10193
  function turnProgressStorePath() {
10129
10194
  if (pathOverride4) return pathOverride4;
10130
- const stateRoot = process.env.XDG_STATE_HOME || join10(homedir10(), ".local", "state");
10131
- return join10(stateRoot, "opencode-claude-code-plugin", "turn-progress.json");
10195
+ const stateRoot = process.env.XDG_STATE_HOME || join11(homedir10(), ".local", "state");
10196
+ return join11(stateRoot, "opencode-claude-code-plugin", "turn-progress.json");
10132
10197
  }
10133
10198
  function isProgress(value) {
10134
10199
  const record = value;
@@ -10158,7 +10223,7 @@ function recordTurnProgress(tag, progress, now = Date.now()) {
10158
10223
  merged.set(tag, { ...record, out: Math.round(record.out) });
10159
10224
  const kept = [...merged.entries()].slice(-MAX_TURN_PROGRESS);
10160
10225
  try {
10161
- mkdirSync6(join10(file, ".."), { recursive: true, mode: 448 });
10226
+ mkdirSync6(join11(file, ".."), { recursive: true, mode: 448 });
10162
10227
  const tmp = `${file}.${process.pid}.tmp`;
10163
10228
  writeFileSync6(tmp, JSON.stringify({ version: 1, turns: Object.fromEntries(kept) }), { mode: 384 });
10164
10229
  renameSync5(tmp, file);
@@ -10176,8 +10241,8 @@ var SESSION_TAG = /^[0-9a-f]{8}$/;
10176
10241
  var pathOverride5 = null;
10177
10242
  function turnHistoryStorePath() {
10178
10243
  if (pathOverride5) return pathOverride5;
10179
- const stateRoot = process.env.XDG_STATE_HOME || join11(homedir11(), ".local", "state");
10180
- return join11(stateRoot, "opencode-claude-code-plugin", "turn-history.json");
10244
+ const stateRoot = process.env.XDG_STATE_HOME || join12(homedir11(), ".local", "state");
10245
+ return join12(stateRoot, "opencode-claude-code-plugin", "turn-history.json");
10181
10246
  }
10182
10247
  function turnHistoryKey(agent, model) {
10183
10248
  return `${agent || "main"}/${model}`;
@@ -10361,7 +10426,7 @@ function recordTurnSample(key, sample) {
10361
10426
  merged.set(key, samples);
10362
10427
  const kept = bounded2(merged);
10363
10428
  try {
10364
- mkdirSync7(join11(file, ".."), { recursive: true, mode: 448 });
10429
+ mkdirSync7(join12(file, ".."), { recursive: true, mode: 448 });
10365
10430
  const tmp = `${file}.${process.pid}.tmp`;
10366
10431
  writeFileSync7(tmp, JSON.stringify({ version: 1, turns: Object.fromEntries(kept) }), { mode: 384 });
10367
10432
  renameSync6(tmp, file);
@@ -12785,7 +12850,7 @@ import { EventEmitter as EventEmitter4 } from "events";
12785
12850
  import { randomUUID as randomUUID5 } from "crypto";
12786
12851
  import { existsSync as existsSync4, writeFileSync as writeFileSync9 } from "fs";
12787
12852
  import { unlink as unlink2 } from "fs/promises";
12788
- import { join as join16 } from "path";
12853
+ import { join as join17 } from "path";
12789
12854
 
12790
12855
  // src/claude-session-bun.ts
12791
12856
  import * as os6 from "os";
@@ -13967,7 +14032,7 @@ ${text2}`
13967
14032
  }
13968
14033
  function stageImage(data, extension) {
13969
14034
  try {
13970
- const file = join16(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
14035
+ const file = join17(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
13971
14036
  writeFileSync9(file, data, { mode: 384, flag: "wx" });
13972
14037
  return file;
13973
14038
  } catch (err) {
@@ -14451,15 +14516,15 @@ function spawnInteractiveProcess(opts) {
14451
14516
  // src/spawn-record-store.ts
14452
14517
  import { mkdirSync as mkdirSync9, readFileSync as readFileSync11, renameSync as renameSync7, statSync as statSync10, writeFileSync as writeFileSync10 } from "fs";
14453
14518
  import { homedir as homedir14 } from "os";
14454
- import { join as join17 } from "path";
14519
+ import { join as join18 } from "path";
14455
14520
  var MAX_SPAWN_RECORDS = 256;
14456
14521
  var SPAWN_RECORD_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
14457
14522
  var pathOverride6 = null;
14458
14523
  var lastWritten = /* @__PURE__ */ new Map();
14459
14524
  function spawnRecordStorePath() {
14460
14525
  if (pathOverride6) return pathOverride6;
14461
- const stateRoot = process.env.XDG_STATE_HOME || join17(homedir14(), ".local", "state");
14462
- return join17(stateRoot, "opencode-claude-code-plugin", "session-spawns.json");
14526
+ const stateRoot = process.env.XDG_STATE_HOME || join18(homedir14(), ".local", "state");
14527
+ return join18(stateRoot, "opencode-claude-code-plugin", "session-spawns.json");
14463
14528
  }
14464
14529
  function isRecord5(value) {
14465
14530
  const record = value;
@@ -14511,7 +14576,7 @@ ${record.agent ?? ""}`;
14511
14576
  const cutoff = now - SPAWN_RECORD_MAX_AGE_MS;
14512
14577
  const kept = [...merged.entries()].filter(([, entry]) => entry.at >= cutoff).sort((a, b) => a[1].at - b[1].at).slice(-MAX_SPAWN_RECORDS);
14513
14578
  try {
14514
- mkdirSync9(join17(file, ".."), { recursive: true, mode: 448 });
14579
+ mkdirSync9(join18(file, ".."), { recursive: true, mode: 448 });
14515
14580
  const tmp = `${file}.${process.pid}.tmp`;
14516
14581
  writeFileSync10(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
14517
14582
  mode: 384
@@ -14589,7 +14654,7 @@ function dedupe2(dirs) {
14589
14654
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
14590
14655
  import { homedir as homedir15 } from "os";
14591
14656
  import { randomUUID as randomUUID7 } from "crypto";
14592
- import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join18 } from "path";
14657
+ import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join19 } from "path";
14593
14658
  function readPromptFileIfPresent(path15) {
14594
14659
  try {
14595
14660
  const content = readFileSync12(path15, "utf8").trim();
@@ -14601,7 +14666,7 @@ function readPromptFileIfPresent(path15) {
14601
14666
  function nearestWorkspaceAgentsPrompt(cwd) {
14602
14667
  let dir = cwd;
14603
14668
  while (true) {
14604
- const content = readPromptFileIfPresent(join18(dir, "AGENTS.md"));
14669
+ const content = readPromptFileIfPresent(join19(dir, "AGENTS.md"));
14605
14670
  if (content) return content;
14606
14671
  const parent = dirname6(dir);
14607
14672
  if (parent === dir) return void 0;
@@ -14753,8 +14818,8 @@ ${options.compressionSummary.trim()}`
14753
14818
  for (const s of extraSystemContent) {
14754
14819
  if (s.trim()) parts.push(s.trim());
14755
14820
  }
14756
- const configRoot = process.env.XDG_CONFIG_HOME ?? join18(homedir15(), ".config");
14757
- const globalAgents = readPromptFileIfPresent(join18(configRoot, "opencode", "AGENTS.md"));
14821
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join19(homedir15(), ".config");
14822
+ const globalAgents = readPromptFileIfPresent(join19(configRoot, "opencode", "AGENTS.md"));
14758
14823
  const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
14759
14824
  const forwarded = extraSystemContent.join("\n\n");
14760
14825
  const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents);
@@ -14770,7 +14835,7 @@ ${options.compressionSummary.trim()}`
14770
14835
  if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
14771
14836
  const content = parts.join("\n\n");
14772
14837
  if (!content) return void 0;
14773
- const path15 = join18(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
14838
+ const path15 = join19(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
14774
14839
  try {
14775
14840
  writeFileSync11(path15, content, { encoding: "utf8", mode: 384 });
14776
14841
  return path15;
@@ -18614,7 +18679,7 @@ var ClaudeCodeLanguageModel = class {
18614
18679
  let replayReason = "no-session-for-key";
18615
18680
  if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
18616
18681
  const configDir = resolveConfigDir(this.config.configDir);
18617
- const transcriptPath2 = (id, dir) => join19(dir ?? configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
18682
+ const transcriptPath2 = (id, dir) => join20(dir ?? configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
18618
18683
  const resumePoint = findResumePoint({
18619
18684
  sessionKey: sk,
18620
18685
  prompt: options.prompt,
@@ -20289,7 +20354,7 @@ import {
20289
20354
  writeFileSync as writeFileSync12
20290
20355
  } from "fs";
20291
20356
  import { homedir as homedir16 } from "os";
20292
- import { dirname as dirname7, join as join20, resolve as resolve6 } from "path";
20357
+ import { dirname as dirname7, join as join21, resolve as resolve6 } from "path";
20293
20358
  import { fileURLToPath as fileURLToPath4 } from "url";
20294
20359
  var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
20295
20360
  var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
@@ -20297,14 +20362,14 @@ var alreadyRan = false;
20297
20362
  function candidateCacheRoots() {
20298
20363
  const xdg = process.env.XDG_CACHE_HOME;
20299
20364
  return [
20300
- xdg ? join20(xdg, "opencode") : null,
20301
- join20(homedir16(), ".cache", "opencode"),
20302
- join20(homedir16(), "Library", "Caches", "opencode")
20365
+ xdg ? join21(xdg, "opencode") : null,
20366
+ join21(homedir16(), ".cache", "opencode"),
20367
+ join21(homedir16(), "Library", "Caches", "opencode")
20303
20368
  ].filter((p) => Boolean(p));
20304
20369
  }
20305
20370
  function userOpencodeJsonPath() {
20306
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join20(homedir16(), ".config");
20307
- return join20(xdgConfig, "opencode", "opencode.json");
20371
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join21(homedir16(), ".config");
20372
+ return join21(xdgConfig, "opencode", "opencode.json");
20308
20373
  }
20309
20374
  function userIntendsToUseUnscoped() {
20310
20375
  const cfg = userOpencodeJsonPath();
@@ -20331,15 +20396,15 @@ function ourLoadedDir() {
20331
20396
  function ourVersion(dir) {
20332
20397
  if (!dir) return null;
20333
20398
  try {
20334
- const pkg = JSON.parse(readFileSync14(join20(dir, "package.json"), "utf8"));
20399
+ const pkg = JSON.parse(readFileSync14(join21(dir, "package.json"), "utf8"));
20335
20400
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
20336
20401
  } catch {
20337
20402
  return null;
20338
20403
  }
20339
20404
  }
20340
20405
  function markerPath() {
20341
- const stateRoot = process.env.XDG_STATE_HOME ?? join20(homedir16(), ".local", "state");
20342
- return join20(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
20406
+ const stateRoot = process.env.XDG_STATE_HOME ?? join21(homedir16(), ".local", "state");
20407
+ return join21(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
20343
20408
  }
20344
20409
  function markerVersion() {
20345
20410
  try {
@@ -20388,7 +20453,7 @@ function cleanupStaleUnscopedInstall() {
20388
20453
  }
20389
20454
  function cleanupOne(cacheRoot, ourDir) {
20390
20455
  if (!existsSync5(cacheRoot)) return;
20391
- const stalePath = join20(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
20456
+ const stalePath = join21(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
20392
20457
  if (!existsSync5(stalePath)) return;
20393
20458
  let realStalePath = stalePath;
20394
20459
  try {
@@ -20396,7 +20461,7 @@ function cleanupOne(cacheRoot, ourDir) {
20396
20461
  } catch {
20397
20462
  }
20398
20463
  if (ourDir && realStalePath === ourDir) return;
20399
- const pkgJsonPath = join20(stalePath, "package.json");
20464
+ const pkgJsonPath = join21(stalePath, "package.json");
20400
20465
  if (!existsSync5(pkgJsonPath)) return;
20401
20466
  let pkg = {};
20402
20467
  try {
@@ -20416,7 +20481,7 @@ function cleanupOne(cacheRoot, ourDir) {
20416
20481
  });
20417
20482
  return;
20418
20483
  }
20419
- const cachePkgJson = join20(cacheRoot, "package.json");
20484
+ const cachePkgJson = join21(cacheRoot, "package.json");
20420
20485
  if (!existsSync5(cachePkgJson)) return;
20421
20486
  try {
20422
20487
  const cfg = JSON.parse(readFileSync14(cachePkgJson, "utf8"));
@@ -20822,22 +20887,37 @@ function stoppedSessionIdV2(event) {
20822
20887
  const id = event.data?.sessionID;
20823
20888
  return typeof id === "string" && id.length > 0 ? id : void 0;
20824
20889
  }
20890
+ function handleSessionEventHere(event) {
20891
+ noteSessionRunEvent(event);
20892
+ const deleted = deletedSessionIdV2(event);
20893
+ if (deleted) {
20894
+ const released = deleteActiveProcessesForSession(deleted);
20895
+ if (released.length > 0) {
20896
+ log.info("released claude state for deleted session", { sessionID: deleted, released });
20897
+ }
20898
+ return;
20899
+ }
20900
+ const stopped = stoppedSessionIdV2(event);
20901
+ if (stopped) releaseParkedProxyCallsForSession(stopped);
20902
+ }
20903
+ var closeOwnFeed;
20904
+ defineOwnRelayMember({
20905
+ handle: (event) => handleSessionEventHere(event),
20906
+ feedOpened: () => {
20907
+ closeOwnFeed ??= openSessionRunEventFeed();
20908
+ },
20909
+ feedClosed: () => {
20910
+ const close = closeOwnFeed;
20911
+ closeOwnFeed = void 0;
20912
+ close?.();
20913
+ }
20914
+ });
20825
20915
  async function followSessionEvents(subscribe, signal) {
20826
- const closeFeed = openSessionRunEventFeed();
20916
+ const closeFollower = openRelayFollower();
20827
20917
  try {
20828
20918
  for await (const event of subscribe({ signal })) {
20829
20919
  if (signal.aborted) break;
20830
- noteSessionRunEvent(event);
20831
- const deleted = deletedSessionIdV2(event);
20832
- if (deleted) {
20833
- const released = deleteActiveProcessesForSession(deleted);
20834
- if (released.length > 0) {
20835
- log.info("released claude state for deleted session", { sessionID: deleted, released });
20836
- }
20837
- continue;
20838
- }
20839
- const stopped = stoppedSessionIdV2(event);
20840
- if (stopped) releaseParkedProxyCallsForSession(stopped);
20920
+ relaySessionEvent(event);
20841
20921
  }
20842
20922
  } catch (err) {
20843
20923
  if (!signal.aborted) {
@@ -20847,7 +20927,7 @@ async function followSessionEvents(subscribe, signal) {
20847
20927
  );
20848
20928
  }
20849
20929
  } finally {
20850
- closeFeed();
20930
+ closeFollower();
20851
20931
  }
20852
20932
  }
20853
20933
  var noticeEmitters = /* @__PURE__ */ new Set();