codex-to-im 0.1.8 → 1.0.10

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/dist/daemon.mjs +96 -16
  2. package/package.json +1 -1
  3. package/SKILL.md +0 -79
package/dist/daemon.mjs CHANGED
@@ -16909,18 +16909,81 @@ function formatMirrorStatus(session) {
16909
16909
  }
16910
16910
  return "\u672A\u76D1\u542C";
16911
16911
  }
16912
- function markMirrorSuppressed(sessionId, durationMs = MIRROR_SUPPRESSION_WINDOW_MS) {
16913
- getState().mirrorSuppressUntil.set(sessionId, Date.now() + durationMs);
16912
+ function normalizeMirrorPromptText(text2) {
16913
+ return text2.replace(/\r\n/g, "\n").trim();
16914
16914
  }
16915
- function isMirrorSuppressed(sessionId) {
16915
+ function beginMirrorSuppression(sessionId, promptText) {
16916
+ getState().mirrorSuppressUntil.set(sessionId, {
16917
+ until: Number.POSITIVE_INFINITY,
16918
+ promptText: normalizeMirrorPromptText(promptText) || null,
16919
+ awaitingPromptMatch: true,
16920
+ droppingTurn: false
16921
+ });
16922
+ }
16923
+ function settleMirrorSuppression(sessionId, durationMs = MIRROR_SUPPRESSION_WINDOW_MS) {
16916
16924
  const state = getState();
16917
- const until = state.mirrorSuppressUntil.get(sessionId);
16918
- if (!until) return false;
16919
- if (until <= Date.now()) {
16925
+ const existing = state.mirrorSuppressUntil.get(sessionId);
16926
+ if (existing) {
16927
+ existing.until = Date.now() + durationMs;
16928
+ return;
16929
+ }
16930
+ state.mirrorSuppressUntil.set(sessionId, {
16931
+ until: Date.now() + durationMs,
16932
+ promptText: null,
16933
+ awaitingPromptMatch: false,
16934
+ droppingTurn: false
16935
+ });
16936
+ }
16937
+ function getMirrorSuppressionState(sessionId) {
16938
+ const state = getState();
16939
+ const suppression = state.mirrorSuppressUntil.get(sessionId);
16940
+ if (!suppression) return null;
16941
+ if (suppression.until <= Date.now()) {
16920
16942
  state.mirrorSuppressUntil.delete(sessionId);
16921
- return false;
16943
+ return null;
16922
16944
  }
16923
- return true;
16945
+ return suppression;
16946
+ }
16947
+ function isMirrorSuppressed(sessionId) {
16948
+ return getMirrorSuppressionState(sessionId) !== null;
16949
+ }
16950
+ function filterSuppressedMirrorRecords(sessionId, records) {
16951
+ const suppression = getMirrorSuppressionState(sessionId);
16952
+ if (!suppression || records.length === 0) return records;
16953
+ const filtered = [];
16954
+ for (const record of records) {
16955
+ const normalizedContent = record.type === "message" ? normalizeMirrorPromptText(record.content || "") : "";
16956
+ if (suppression.droppingTurn) {
16957
+ if (record.type === "message" && record.role === "user") {
16958
+ if (suppression.promptText && normalizedContent === suppression.promptText) {
16959
+ continue;
16960
+ }
16961
+ filtered.push(record);
16962
+ continue;
16963
+ }
16964
+ if (record.type === "task_complete") {
16965
+ suppression.droppingTurn = false;
16966
+ suppression.awaitingPromptMatch = false;
16967
+ suppression.promptText = null;
16968
+ continue;
16969
+ }
16970
+ continue;
16971
+ }
16972
+ if (suppression.awaitingPromptMatch) {
16973
+ if (record.type === "message" && record.role === "user") {
16974
+ if (suppression.promptText && normalizedContent === suppression.promptText) {
16975
+ suppression.awaitingPromptMatch = false;
16976
+ suppression.droppingTurn = true;
16977
+ continue;
16978
+ }
16979
+ filtered.push(record);
16980
+ continue;
16981
+ }
16982
+ continue;
16983
+ }
16984
+ filtered.push(record);
16985
+ }
16986
+ return filtered;
16924
16987
  }
16925
16988
  function resetMirrorReadState(subscription) {
16926
16989
  subscription.fileOffset = 0;
@@ -16928,6 +16991,7 @@ function resetMirrorReadState(subscription) {
16928
16991
  subscription.fileMtimeMs = null;
16929
16992
  subscription.fileIdentity = null;
16930
16993
  subscription.trailingText = "";
16994
+ subscription.bufferedRecords = [];
16931
16995
  }
16932
16996
  function statMirrorFile(filePath) {
16933
16997
  try {
@@ -17262,6 +17326,19 @@ function flushTimedOutMirrorTurn(subscription, nowMs = Date.now()) {
17262
17326
  "interrupted"
17263
17327
  );
17264
17328
  }
17329
+ function hasPendingMirrorWork(subscription) {
17330
+ return subscription.bufferedRecords.length > 0 || subscription.pendingTurn !== null;
17331
+ }
17332
+ function consumeBufferedMirrorTurns(subscription, nowMs = Date.now()) {
17333
+ const bufferedRecords = subscription.bufferedRecords;
17334
+ subscription.bufferedRecords = [];
17335
+ const finalizedTurns = bufferedRecords.length > 0 ? consumeMirrorRecords(subscription, bufferedRecords) : [];
17336
+ const timedOutTurn = flushTimedOutMirrorTurn(subscription, nowMs);
17337
+ if (timedOutTurn) {
17338
+ finalizedTurns.push(timedOutTurn);
17339
+ }
17340
+ return finalizedTurns;
17341
+ }
17265
17342
  function removeMirrorSubscription(bindingId) {
17266
17343
  const state = getState();
17267
17344
  const existing = state.mirrorSubscriptions.get(bindingId);
@@ -17307,6 +17384,7 @@ function upsertMirrorSubscription(binding) {
17307
17384
  fileMtimeMs: null,
17308
17385
  fileIdentity: null,
17309
17386
  trailingText: "",
17387
+ bufferedRecords: [],
17310
17388
  pendingTurn: null
17311
17389
  };
17312
17390
  watchMirrorFile(created, filePath);
@@ -17392,7 +17470,7 @@ async function reconcileMirrorSubscription(subscription) {
17392
17470
  return;
17393
17471
  }
17394
17472
  const unchanged = !subscription.dirty && subscription.fileIdentity === snapshot.identity && subscription.fileSize === snapshot.size && subscription.fileMtimeMs === snapshot.mtimeMs;
17395
- if (unchanged) {
17473
+ if (unchanged && !hasPendingMirrorWork(subscription)) {
17396
17474
  syncMirrorSessionState(subscription.sessionId);
17397
17475
  return;
17398
17476
  }
@@ -17423,15 +17501,17 @@ async function reconcileMirrorSubscription(subscription) {
17423
17501
  subscription.fileMtimeMs = snapshot.mtimeMs;
17424
17502
  subscription.fileIdentity = snapshot.identity;
17425
17503
  subscription.dirty = false;
17504
+ if (deliverableRecords.length > 0) {
17505
+ const filteredRecords = filterSuppressedMirrorRecords(subscription.sessionId, deliverableRecords);
17506
+ if (filteredRecords.length > 0) {
17507
+ subscription.bufferedRecords.push(...filteredRecords);
17508
+ }
17509
+ }
17426
17510
  if (getState().activeTasks.has(subscription.sessionId) || isMirrorSuppressed(subscription.sessionId)) {
17427
17511
  syncMirrorSessionState(subscription.sessionId);
17428
17512
  return;
17429
17513
  }
17430
- const finalizedTurns = deliverableRecords.length > 0 ? consumeMirrorRecords(subscription, deliverableRecords) : [];
17431
- const timedOutTurn = flushTimedOutMirrorTurn(subscription);
17432
- if (timedOutTurn) {
17433
- finalizedTurns.push(timedOutTurn);
17434
- }
17514
+ const finalizedTurns = consumeBufferedMirrorTurns(subscription);
17435
17515
  if (finalizedTurns.length === 0) {
17436
17516
  syncMirrorSessionState(subscription.sessionId);
17437
17517
  return;
@@ -17811,7 +17891,7 @@ async function handleMessage(adapter, msg) {
17811
17891
  const taskAbort = new AbortController();
17812
17892
  const state = getState();
17813
17893
  state.activeTasks.set(binding.codepilotSessionId, taskAbort);
17814
- markMirrorSuppressed(binding.codepilotSessionId);
17894
+ beginMirrorSuppression(binding.codepilotSessionId, text2 || (hasAttachments ? "Describe this image." : ""));
17815
17895
  syncSessionRuntimeState(binding.codepilotSessionId);
17816
17896
  let previewState = null;
17817
17897
  const caps = adapter.getPreviewCapabilities?.(msg.address.chatId) ?? null;
@@ -17946,7 +18026,7 @@ async function handleMessage(adapter, msg) {
17946
18026
  } catch {
17947
18027
  }
17948
18028
  }
17949
- markMirrorSuppressed(binding.codepilotSessionId);
18029
+ settleMirrorSuppression(binding.codepilotSessionId);
17950
18030
  state.activeTasks.delete(binding.codepilotSessionId);
17951
18031
  syncSessionRuntimeState(binding.codepilotSessionId);
17952
18032
  adapter.onMessageEnd?.(msg.address.chatId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-to-im",
3
- "version": "0.1.8",
3
+ "version": "1.0.10",
4
4
  "description": "Installable Codex-to-IM bridge with local setup UI and background service",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/SKILL.md DELETED
@@ -1,79 +0,0 @@
1
- ---
2
- name: codex-to-im
3
- description: |
4
- Optional Codex integration for the codex-to-im local app. Use only when the
5
- user wants to open codex-to-im from Codex or enter the Feishu session-sharing
6
- flow. Do not use this skill as the main setup, config, logs, or daemon
7
- management path.
8
- argument-hint: "open | share-feishu"
9
- allowed-tools:
10
- - Bash
11
- - Read
12
- - Grep
13
- - Glob
14
- ---
15
-
16
- # Codex-to-IM Optional Integration
17
-
18
- `codex-to-im` is a standalone local app with a background bridge and web workbench.
19
-
20
- This optional integration is intentionally thin. It exists only for two actions:
21
-
22
- - `open`
23
- - `share-feishu`
24
-
25
- If the user asks to configure channels, start or stop the bridge, inspect logs, or diagnose issues, do not treat this integration as the main workflow. Open the local app instead.
26
-
27
- ## Resolve the repo / install root
28
-
29
- Prefer these locations:
30
-
31
- - `~/.codex/skills/codex-to-im`
32
- - `~/.claude/skills/codex-to-im`
33
-
34
- If neither exists, fall back to globbing `**/skills/**/codex-to-im/SKILL.md` and derive the root from that match.
35
-
36
- ## Command mapping
37
-
38
- Map user intent to one of these two actions:
39
-
40
- | User says | Action |
41
- |---|---|
42
- | `open codex-to-im`, `打开 codex-to-im`, `open bridge workbench`, `打开工作台` | `open` |
43
- | `share current session to feishu`, `共享当前会话到飞书`, `把当前会话发到飞书`, `open feishu handoff` | `share-feishu` |
44
-
45
- ## Execution
46
-
47
- ### `open`
48
-
49
- Prefer:
50
-
51
- ```bash
52
- codex-to-im open
53
- ```
54
-
55
- If the `codex-to-im` command is unavailable, fall back to:
56
-
57
- ```bash
58
- node dist/cli.mjs open
59
- ```
60
-
61
- Run from the repo / install root.
62
-
63
- ### `share-feishu`
64
-
65
- Prefer:
66
-
67
- ```bash
68
- codex-to-im share-feishu
69
- ```
70
-
71
- If the `codex-to-im` command is unavailable, fall back to:
72
-
73
- ```bash
74
- node dist/cli.mjs share-feishu
75
- ```
76
-
77
- Run from the repo / install root.
78
-
79
- After opening the workbench, tell the user to use the desktop sessions panel or the IM binding panel to finish binding the target thread to Feishu.