oh-my-codex 0.11.0 → 0.11.2

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 (180) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/crates/omx-explore/src/main.rs +115 -9
  4. package/dist/cli/__tests__/ask.test.js +3 -3
  5. package/dist/cli/__tests__/ask.test.js.map +1 -1
  6. package/dist/cli/__tests__/doctor-team.test.js +12 -2
  7. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  8. package/dist/cli/__tests__/exec.test.js +4 -0
  9. package/dist/cli/__tests__/exec.test.js.map +1 -1
  10. package/dist/cli/__tests__/hooks.test.d.ts +2 -0
  11. package/dist/cli/__tests__/hooks.test.d.ts.map +1 -0
  12. package/dist/cli/__tests__/hooks.test.js +55 -0
  13. package/dist/cli/__tests__/hooks.test.js.map +1 -0
  14. package/dist/cli/__tests__/index.test.js +6 -2
  15. package/dist/cli/__tests__/index.test.js.map +1 -1
  16. package/dist/cli/__tests__/package-bin-contract.test.js +3 -10
  17. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  18. package/dist/cli/__tests__/sparkshell-packaging.test.js +1 -1
  19. package/dist/cli/__tests__/sparkshell-packaging.test.js.map +1 -1
  20. package/dist/cli/__tests__/team.test.js +4 -4
  21. package/dist/cli/__tests__/team.test.js.map +1 -1
  22. package/dist/cli/ask.js +1 -1
  23. package/dist/cli/ask.js.map +1 -1
  24. package/dist/cli/hooks.d.ts.map +1 -1
  25. package/dist/cli/hooks.js +2 -2
  26. package/dist/cli/hooks.js.map +1 -1
  27. package/dist/cli/index.d.ts.map +1 -1
  28. package/dist/cli/index.js +26 -12
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/cli/tmux-hook.d.ts.map +1 -1
  31. package/dist/cli/tmux-hook.js +5 -4
  32. package/dist/cli/tmux-hook.js.map +1 -1
  33. package/dist/hooks/__tests__/codebase-map.test.js +1 -1
  34. package/dist/hooks/__tests__/codebase-map.test.js.map +1 -1
  35. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +46 -36
  36. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  37. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +6 -0
  38. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  39. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +24 -24
  40. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  41. package/dist/hooks/__tests__/notify-hook-linked-sync.test.js +45 -1
  42. package/dist/hooks/__tests__/notify-hook-linked-sync.test.js.map +1 -1
  43. package/dist/hooks/__tests__/notify-hook-modules.test.js +1 -1
  44. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  45. package/dist/hooks/__tests__/notify-hook-native-dispatch-contract.test.js +5 -5
  46. package/dist/hooks/__tests__/notify-hook-native-dispatch-contract.test.js.map +1 -1
  47. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +22 -1
  48. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  49. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +126 -1
  50. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  51. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +5 -4
  52. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  53. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +141 -3
  54. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  55. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +11 -8
  56. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  57. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js +1 -1
  58. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  59. package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js +1 -1
  60. package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js.map +1 -1
  61. package/dist/hooks/__tests__/tmux-hook-engine.test.js +65 -1
  62. package/dist/hooks/__tests__/tmux-hook-engine.test.js.map +1 -1
  63. package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.d.ts +2 -0
  64. package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.d.ts.map +1 -0
  65. package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.js +32 -0
  66. package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.js.map +1 -0
  67. package/dist/hooks/extensibility/__tests__/sdk.test.js +172 -0
  68. package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
  69. package/dist/hooks/extensibility/sdk/logging.d.ts +6 -0
  70. package/dist/hooks/extensibility/sdk/logging.d.ts.map +1 -0
  71. package/dist/hooks/extensibility/sdk/logging.js +32 -0
  72. package/dist/hooks/extensibility/sdk/logging.js.map +1 -0
  73. package/dist/hooks/extensibility/sdk/paths.d.ts +7 -0
  74. package/dist/hooks/extensibility/sdk/paths.d.ts.map +1 -0
  75. package/dist/hooks/extensibility/sdk/paths.js +22 -0
  76. package/dist/hooks/extensibility/sdk/paths.js.map +1 -0
  77. package/dist/hooks/extensibility/sdk/plugin-state.d.ts +5 -0
  78. package/dist/hooks/extensibility/sdk/plugin-state.d.ts.map +1 -0
  79. package/dist/hooks/extensibility/sdk/plugin-state.js +66 -0
  80. package/dist/hooks/extensibility/sdk/plugin-state.js.map +1 -0
  81. package/dist/hooks/extensibility/sdk/runtime-state.d.ts +3 -0
  82. package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -0
  83. package/dist/hooks/extensibility/sdk/runtime-state.js +41 -0
  84. package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -0
  85. package/dist/hooks/extensibility/sdk/tmux.d.ts +10 -0
  86. package/dist/hooks/extensibility/sdk/tmux.d.ts.map +1 -0
  87. package/dist/hooks/extensibility/sdk/tmux.js +178 -0
  88. package/dist/hooks/extensibility/sdk/tmux.js.map +1 -0
  89. package/dist/hooks/extensibility/sdk.d.ts.map +1 -1
  90. package/dist/hooks/extensibility/sdk.js +14 -246
  91. package/dist/hooks/extensibility/sdk.js.map +1 -1
  92. package/dist/hooks/extensibility/types.d.ts +52 -0
  93. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  94. package/dist/hud/__tests__/authority.test.js +2 -2
  95. package/dist/hud/__tests__/authority.test.js.map +1 -1
  96. package/dist/hud/__tests__/index.test.js +9 -2
  97. package/dist/hud/__tests__/index.test.js.map +1 -1
  98. package/dist/mcp/__tests__/state-server-schema.test.js +10 -0
  99. package/dist/mcp/__tests__/state-server-schema.test.js.map +1 -1
  100. package/dist/mcp/__tests__/state-server.test.js +152 -1
  101. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  102. package/dist/mcp/state-server.d.ts +4 -4
  103. package/dist/mcp/state-server.d.ts.map +1 -1
  104. package/dist/mcp/state-server.js +1 -0
  105. package/dist/mcp/state-server.js.map +1 -1
  106. package/dist/modes/__tests__/base-autoresearch-contract.test.js +17 -0
  107. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  108. package/dist/modes/base.d.ts +2 -2
  109. package/dist/modes/base.d.ts.map +1 -1
  110. package/dist/modes/base.js +1 -1
  111. package/dist/notifications/__tests__/reply-listener.test.js +375 -8
  112. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  113. package/dist/notifications/reply-listener.d.ts +57 -0
  114. package/dist/notifications/reply-listener.d.ts.map +1 -1
  115. package/dist/notifications/reply-listener.js +85 -40
  116. package/dist/notifications/reply-listener.js.map +1 -1
  117. package/dist/scripts/__tests__/smoke-packed-install.test.js +8 -41
  118. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  119. package/dist/scripts/__tests__/test-reply-listener-live.test.d.ts +2 -0
  120. package/dist/scripts/__tests__/test-reply-listener-live.test.d.ts.map +1 -0
  121. package/dist/scripts/__tests__/test-reply-listener-live.test.js +82 -0
  122. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -0
  123. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  124. package/dist/scripts/notify-hook/team-dispatch.js +7 -3
  125. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  126. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  127. package/dist/scripts/notify-hook/team-leader-nudge.js +4 -3
  128. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  129. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  130. package/dist/scripts/notify-hook/team-worker.js +27 -8
  131. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  132. package/dist/scripts/notify-hook/tmux-injection.d.ts +0 -1
  133. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  134. package/dist/scripts/notify-hook/tmux-injection.js +123 -112
  135. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  136. package/dist/scripts/smoke-packed-install.d.ts +1 -3
  137. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  138. package/dist/scripts/smoke-packed-install.js +14 -155
  139. package/dist/scripts/smoke-packed-install.js.map +1 -1
  140. package/dist/scripts/test-reply-listener-live.d.ts +24 -0
  141. package/dist/scripts/test-reply-listener-live.d.ts.map +1 -0
  142. package/dist/scripts/test-reply-listener-live.js +138 -0
  143. package/dist/scripts/test-reply-listener-live.js.map +1 -0
  144. package/dist/scripts/tmux-hook-engine.d.ts +3 -3
  145. package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
  146. package/dist/scripts/tmux-hook-engine.js +17 -16
  147. package/dist/scripts/tmux-hook-engine.js.map +1 -1
  148. package/dist/team/__tests__/hardening-e2e.test.js +11 -1
  149. package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
  150. package/dist/team/__tests__/mcp-comm.test.js +11 -1
  151. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  152. package/dist/team/__tests__/runtime.test.js +11 -1
  153. package/dist/team/__tests__/runtime.test.js.map +1 -1
  154. package/dist/team/__tests__/state.test.js +9 -1
  155. package/dist/team/__tests__/state.test.js.map +1 -1
  156. package/dist/team/__tests__/tmux-test-fixture.d.ts +9 -3
  157. package/dist/team/__tests__/tmux-test-fixture.d.ts.map +1 -1
  158. package/dist/team/__tests__/tmux-test-fixture.js +69 -24
  159. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  160. package/dist/team/__tests__/tmux-test-fixture.test.js +80 -3
  161. package/dist/team/__tests__/tmux-test-fixture.test.js.map +1 -1
  162. package/dist/team/tmux-session.d.ts.map +1 -1
  163. package/dist/team/tmux-session.js +2 -7
  164. package/dist/team/tmux-session.js.map +1 -1
  165. package/dist/verification/__tests__/ci-rust-gates.test.js +13 -0
  166. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  167. package/dist/verification/__tests__/explore-harness-release-workflow.test.js +15 -6
  168. package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
  169. package/dist/verification/__tests__/native-release-manifest.test.js +1 -1
  170. package/dist/verification/__tests__/native-release-manifest.test.js.map +1 -1
  171. package/package.json +2 -1
  172. package/src/scripts/__tests__/smoke-packed-install.test.ts +9 -50
  173. package/src/scripts/__tests__/test-reply-listener-live.test.ts +101 -0
  174. package/src/scripts/notify-hook/team-dispatch.ts +8 -2
  175. package/src/scripts/notify-hook/team-leader-nudge.ts +4 -3
  176. package/src/scripts/notify-hook/team-worker.ts +26 -8
  177. package/src/scripts/notify-hook/tmux-injection.ts +118 -107
  178. package/src/scripts/smoke-packed-install.ts +17 -164
  179. package/src/scripts/test-reply-listener-live.ts +188 -0
  180. package/src/scripts/tmux-hook-engine.ts +18 -16
@@ -0,0 +1,22 @@
1
+ import { join } from 'path';
2
+ export function sanitizeHookPluginName(name) {
3
+ const cleaned = (name || 'unknown-plugin').replace(/[^a-zA-Z0-9._-]/g, '-');
4
+ return cleaned || 'unknown-plugin';
5
+ }
6
+ export function hookPluginRootDir(cwd, pluginName) {
7
+ return join(cwd, '.omx', 'state', 'hooks', 'plugins', sanitizeHookPluginName(pluginName));
8
+ }
9
+ export function hookPluginTmuxStatePath(cwd, pluginName) {
10
+ return join(hookPluginRootDir(cwd, pluginName), 'tmux.json');
11
+ }
12
+ export function hookPluginDataPath(cwd, pluginName) {
13
+ return join(hookPluginRootDir(cwd, pluginName), 'data.json');
14
+ }
15
+ export function hookPluginLogPath(cwd, now = new Date()) {
16
+ const day = now.toISOString().slice(0, 10);
17
+ return join(cwd, '.omx', 'logs', `hooks-${day}.jsonl`);
18
+ }
19
+ export function omxRootStateFilePath(cwd, fileName) {
20
+ return join(cwd, '.omx', 'state', fileName);
21
+ }
22
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../../src/hooks/extensibility/sdk/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IAC5E,OAAO,OAAO,IAAI,gBAAgB,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,UAAkB;IAC/D,OAAO,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,GAAW,EAAE,UAAkB;IACrE,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,UAAkB;IAChE,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE;IAC7D,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3C,OAAO,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAE,QAAgB;IAChE,OAAO,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { HookPluginSdk } from '../types.js';
2
+ export declare function normalizeHookPluginStateKey(key: string): string;
3
+ export declare function createHookPluginStateApi(cwd: string, pluginName: string): HookPluginSdk['state'];
4
+ export declare function clearHookPluginStateFiles(cwd: string, pluginName: string): Promise<void>;
5
+ //# sourceMappingURL=plugin-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-state.d.ts","sourceRoot":"","sources":["../../../../src/hooks/extensibility/sdk/plugin-state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAYjD,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO/D;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,aAAa,CAAC,OAAO,CAAC,CAsCxB;AAED,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9F"}
@@ -0,0 +1,66 @@
1
+ import { existsSync } from 'fs';
2
+ import { mkdir, readFile, unlink, writeFile } from 'fs/promises';
3
+ import { dirname, join } from 'path';
4
+ import { hookPluginDataPath, hookPluginRootDir, sanitizeHookPluginName } from './paths.js';
5
+ async function readJsonIfExists(path, fallback) {
6
+ if (!existsSync(path))
7
+ return fallback;
8
+ try {
9
+ return JSON.parse(await readFile(path, 'utf-8'));
10
+ }
11
+ catch {
12
+ return fallback;
13
+ }
14
+ }
15
+ export function normalizeHookPluginStateKey(key) {
16
+ const trimmed = key.trim();
17
+ if (!trimmed)
18
+ throw new Error('state key is required');
19
+ if (trimmed.includes('..') || trimmed.startsWith('/')) {
20
+ throw new Error('invalid state key');
21
+ }
22
+ return trimmed;
23
+ }
24
+ export function createHookPluginStateApi(cwd, pluginName) {
25
+ const dataPath = hookPluginDataPath(cwd, pluginName);
26
+ async function readData() {
27
+ return readJsonIfExists(dataPath, {});
28
+ }
29
+ async function writeData(value) {
30
+ await mkdir(dirname(dataPath), { recursive: true });
31
+ await writeFile(dataPath, JSON.stringify(value, null, 2));
32
+ }
33
+ return {
34
+ read: async (key, fallback) => {
35
+ const safeKey = normalizeHookPluginStateKey(key);
36
+ const data = await readData();
37
+ if (!(safeKey in data))
38
+ return fallback;
39
+ return data[safeKey];
40
+ },
41
+ write: async (key, value) => {
42
+ const safeKey = normalizeHookPluginStateKey(key);
43
+ const data = await readData();
44
+ data[safeKey] = value;
45
+ await writeData(data);
46
+ },
47
+ delete: async (key) => {
48
+ const safeKey = normalizeHookPluginStateKey(key);
49
+ const data = await readData();
50
+ if (safeKey in data) {
51
+ delete data[safeKey];
52
+ await writeData(data);
53
+ }
54
+ },
55
+ all: async () => {
56
+ const data = await readData();
57
+ return data;
58
+ },
59
+ };
60
+ }
61
+ export async function clearHookPluginStateFiles(cwd, pluginName) {
62
+ const root = hookPluginRootDir(cwd, sanitizeHookPluginName(pluginName));
63
+ await unlink(join(root, 'data.json')).catch(() => { });
64
+ await unlink(join(root, 'tmux.json')).catch(() => { });
65
+ }
66
+ //# sourceMappingURL=plugin-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-state.js","sourceRoot":"","sources":["../../../../src/hooks/extensibility/sdk/plugin-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { HookPluginSdk } from '../types.js';
2
+ export declare function createHookPluginOmxApi(cwd: string): HookPluginSdk['omx'];
3
+ //# sourceMappingURL=runtime-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-state.d.ts","sourceRoot":"","sources":["../../../../src/hooks/extensibility/sdk/runtime-state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAKV,aAAa,EACd,MAAM,aAAa,CAAC;AA2BrB,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAwBxE"}
@@ -0,0 +1,41 @@
1
+ import { existsSync } from 'fs';
2
+ import { readFile } from 'fs/promises';
3
+ import { omxRootStateFilePath } from './paths.js';
4
+ function isRecord(value) {
5
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
6
+ }
7
+ async function readOmxStateFile(path, normalize) {
8
+ if (!existsSync(path))
9
+ return null;
10
+ try {
11
+ const parsed = JSON.parse(await readFile(path, 'utf-8'));
12
+ if (!isRecord(parsed))
13
+ return null;
14
+ return normalize ? normalize(parsed) : parsed;
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ function normalizeSessionState(value) {
21
+ return typeof value.session_id === 'string' && value.session_id.trim()
22
+ ? value
23
+ : null;
24
+ }
25
+ export function createHookPluginOmxApi(cwd) {
26
+ return {
27
+ session: {
28
+ read: () => readOmxStateFile(omxRootStateFilePath(cwd, 'session.json'), normalizeSessionState),
29
+ },
30
+ hud: {
31
+ read: () => readOmxStateFile(omxRootStateFilePath(cwd, 'hud-state.json')),
32
+ },
33
+ notifyFallback: {
34
+ read: () => readOmxStateFile(omxRootStateFilePath(cwd, 'notify-fallback-state.json')),
35
+ },
36
+ updateCheck: {
37
+ read: () => readOmxStateFile(omxRootStateFilePath(cwd, 'update-check.json')),
38
+ },
39
+ };
40
+ }
41
+ //# sourceMappingURL=runtime-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-state.js","sourceRoot":"","sources":["../../../../src/hooks/extensibility/sdk/runtime-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,IAAY,EACZ,SAAwD;IAExD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAY,CAAC;QACpE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;QACnC,OAAO,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAW,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,KAA8B;IAC3D,OAAO,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE;QACpE,CAAC,CAAC,KAAkC;QACpC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,OAAO;QACL,OAAO,EAAE;YACP,IAAI,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAC1B,oBAAoB,CAAC,GAAG,EAAE,cAAc,CAAC,EACzC,qBAAqB,CACtB;SACF;QACD,GAAG,EAAE;YACH,IAAI,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAC1B,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAC5C;SACF;QACD,cAAc,EAAE;YACd,IAAI,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAC1B,oBAAoB,CAAC,GAAG,EAAE,4BAA4B,CAAC,CACxD;SACF;QACD,WAAW,EAAE;YACX,IAAI,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAC1B,oBAAoB,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAC/C;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { HookEventEnvelope, HookPluginSdk } from '../types.js';
2
+ interface HookPluginTmuxApiOptions {
3
+ cwd: string;
4
+ pluginName: string;
5
+ event: HookEventEnvelope;
6
+ sideEffectsEnabled?: boolean;
7
+ }
8
+ export declare function createHookPluginTmuxApi(options: HookPluginTmuxApiOptions): HookPluginSdk['tmux'];
9
+ export {};
10
+ //# sourceMappingURL=tmux.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux.d.ts","sourceRoot":"","sources":["../../../../src/hooks/extensibility/sdk/tmux.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EAGd,MAAM,aAAa,CAAC;AAarB,UAAU,wBAAwB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,iBAAiB,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AA2LD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CAAC,MAAM,CAAC,CAIhG"}
@@ -0,0 +1,178 @@
1
+ import { existsSync } from 'fs';
2
+ import { createHash } from 'crypto';
3
+ import { mkdir, readFile, writeFile } from 'fs/promises';
4
+ import { dirname } from 'path';
5
+ import { spawnSync } from 'child_process';
6
+ import { sleepSync } from '../../../utils/sleep.js';
7
+ import { resolveCodexPane } from '../../../scripts/tmux-hook-engine.js';
8
+ import { appendHookPluginLog } from './logging.js';
9
+ import { hookPluginTmuxStatePath } from './paths.js';
10
+ const INJECTION_MARKER = '[OMX_TMUX_INJECT]';
11
+ const DEFAULT_COOLDOWN_MS = 15_000;
12
+ const DEFAULT_DEDUPE_WINDOW_MS = 60_000;
13
+ function asPositiveNumber(value, fallback) {
14
+ if (!value)
15
+ return fallback;
16
+ const parsed = Number(value);
17
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
18
+ }
19
+ async function readJsonIfExists(path, fallback) {
20
+ if (!existsSync(path))
21
+ return fallback;
22
+ try {
23
+ return JSON.parse(await readFile(path, 'utf-8'));
24
+ }
25
+ catch {
26
+ return fallback;
27
+ }
28
+ }
29
+ function hashDedupeKey(target, text) {
30
+ return createHash('sha256').update(`${target}|${text}`).digest('hex');
31
+ }
32
+ function sleepFractionalSeconds(seconds) {
33
+ if (!Number.isFinite(seconds) || seconds <= 0)
34
+ return;
35
+ sleepSync(Math.round(seconds * 1000));
36
+ }
37
+ function runTmux(args) {
38
+ const result = spawnSync('tmux', args, { encoding: 'utf-8' });
39
+ if (result.error)
40
+ return { ok: false, stderr: result.error.message };
41
+ if (result.status !== 0) {
42
+ return { ok: false, stderr: (result.stderr || '').trim() || `tmux exited ${result.status}` };
43
+ }
44
+ return { ok: true, stdout: (result.stdout || '').trim() };
45
+ }
46
+ function isHudStartCommand(startCommand) {
47
+ return /\bomx\b.*\bhud\b.*--watch/i.test(startCommand);
48
+ }
49
+ function resolveSessionPaneTarget(sessionName) {
50
+ const paneList = runTmux(['list-panes', '-t', sessionName, '-F', '#{pane_id}\t#{pane_active}\t#{pane_start_command}']);
51
+ if (!paneList.ok) {
52
+ return { ok: false, reason: 'target_missing', error: paneList.stderr };
53
+ }
54
+ const rows = paneList.stdout
55
+ .split('\n')
56
+ .map((line) => line.trim())
57
+ .filter(Boolean)
58
+ .map((line) => {
59
+ const [paneId = '', activeRaw = '0', startCommand = ''] = line.split('\t');
60
+ return {
61
+ paneId,
62
+ active: activeRaw === '1',
63
+ startCommand: startCommand.trim(),
64
+ };
65
+ })
66
+ .filter((row) => row.paneId.startsWith('%'));
67
+ if (rows.length === 0)
68
+ return { ok: false, reason: 'target_missing' };
69
+ const nonHudRows = rows.filter((row) => !isHudStartCommand(row.startCommand));
70
+ const canonicalRows = nonHudRows.filter((row) => /\bcodex\b/i.test(row.startCommand));
71
+ const resolved = canonicalRows.find((row) => row.active)
72
+ || canonicalRows[0]
73
+ || nonHudRows.find((row) => row.active)
74
+ || nonHudRows[0];
75
+ if (!resolved)
76
+ return { ok: false, reason: 'target_missing' };
77
+ return { ok: true, reason: 'ok', target: resolved.paneId, paneId: resolved.paneId };
78
+ }
79
+ function resolveTmuxTarget(options) {
80
+ const paneId = typeof options.paneId === 'string' ? options.paneId.trim() : '';
81
+ if (paneId) {
82
+ const pane = runTmux(['display-message', '-p', '-t', paneId, '#{pane_id}']);
83
+ if (pane.ok && pane.stdout)
84
+ return { ok: true, reason: 'ok', target: pane.stdout, paneId: pane.stdout };
85
+ return { ok: false, reason: 'target_missing', error: pane.ok ? 'pane_not_found' : pane.stderr };
86
+ }
87
+ const sessionName = typeof options.sessionName === 'string' ? options.sessionName.trim() : '';
88
+ if (sessionName) {
89
+ return resolveSessionPaneTarget(sessionName);
90
+ }
91
+ const envPane = String(resolveCodexPane() || '').trim();
92
+ if (envPane)
93
+ return { ok: true, reason: 'ok', target: envPane, paneId: envPane };
94
+ return { ok: false, reason: 'target_missing' };
95
+ }
96
+ async function sendTmuxKeys(options, context) {
97
+ if (!context.sideEffectsEnabled) {
98
+ return { ok: false, reason: 'side_effects_disabled' };
99
+ }
100
+ const text = typeof options.text === 'string' ? options.text : '';
101
+ if (!text.trim()) {
102
+ return { ok: false, reason: 'invalid_text' };
103
+ }
104
+ const marker = process.env.OMX_HOOK_PLUGIN_LOOP_MARKER || '[OMX_HOOK_PLUGIN]';
105
+ if (marker && text.includes(marker)) {
106
+ return { ok: false, reason: 'loop_guard_input_marker' };
107
+ }
108
+ const targetResolution = resolveTmuxTarget(options);
109
+ if (!targetResolution.ok || !targetResolution.target) {
110
+ return targetResolution;
111
+ }
112
+ const tmuxStatePath = hookPluginTmuxStatePath(context.cwd, context.pluginName);
113
+ await mkdir(dirname(tmuxStatePath), { recursive: true });
114
+ const tmuxState = await readJsonIfExists(tmuxStatePath, {
115
+ last_sent_at: 0,
116
+ recent_keys: {},
117
+ });
118
+ const now = Date.now();
119
+ const cooldownMs = typeof options.cooldownMs === 'number'
120
+ ? Math.max(0, options.cooldownMs)
121
+ : asPositiveNumber(process.env.OMX_HOOK_PLUGIN_COOLDOWN_MS, DEFAULT_COOLDOWN_MS);
122
+ const dedupeWindowMs = asPositiveNumber(process.env.OMX_HOOK_PLUGIN_DEDUPE_MS, DEFAULT_DEDUPE_WINDOW_MS);
123
+ const minTs = now - dedupeWindowMs;
124
+ tmuxState.recent_keys = Object.fromEntries(Object.entries(tmuxState.recent_keys || {}).filter(([, ts]) => Number.isFinite(ts) && ts >= minTs));
125
+ if (cooldownMs > 0 && now - (tmuxState.last_sent_at || 0) < cooldownMs) {
126
+ return { ok: false, reason: 'cooldown_active', target: targetResolution.target, paneId: targetResolution.target };
127
+ }
128
+ const dedupeKey = hashDedupeKey(targetResolution.target, text);
129
+ if (tmuxState.recent_keys[dedupeKey]) {
130
+ return { ok: false, reason: 'duplicate_event', target: targetResolution.target, paneId: targetResolution.target };
131
+ }
132
+ const markedText = `${text} ${INJECTION_MARKER}`;
133
+ const typed = runTmux(['send-keys', '-t', targetResolution.target, '-l', markedText]);
134
+ if (!typed.ok) {
135
+ return {
136
+ ok: false,
137
+ reason: 'tmux_failed',
138
+ target: targetResolution.target,
139
+ paneId: targetResolution.target,
140
+ error: typed.stderr,
141
+ };
142
+ }
143
+ if (options.submit !== false) {
144
+ sleepFractionalSeconds(0.12);
145
+ const submitA = runTmux(['send-keys', '-t', targetResolution.target, 'C-m']);
146
+ sleepFractionalSeconds(0.1);
147
+ const submitB = runTmux(['send-keys', '-t', targetResolution.target, 'C-m']);
148
+ if (!submitA.ok && !submitB.ok) {
149
+ return {
150
+ ok: false,
151
+ reason: 'tmux_failed',
152
+ target: targetResolution.target,
153
+ paneId: targetResolution.target,
154
+ error: submitA.stderr || submitB.stderr,
155
+ };
156
+ }
157
+ }
158
+ tmuxState.last_sent_at = now;
159
+ tmuxState.recent_keys[dedupeKey] = now;
160
+ await writeFile(tmuxStatePath, JSON.stringify(tmuxState, null, 2));
161
+ await appendHookPluginLog(context.cwd, context.pluginName, 'info', 'tmux.sendKeys', {
162
+ hook_event: context.event.event,
163
+ target: targetResolution.target,
164
+ submitted: options.submit !== false,
165
+ }).catch(() => { });
166
+ return {
167
+ ok: true,
168
+ reason: 'ok',
169
+ target: targetResolution.target,
170
+ paneId: targetResolution.target,
171
+ };
172
+ }
173
+ export function createHookPluginTmuxApi(options) {
174
+ return {
175
+ sendKeys: (sendOptions) => sendTmuxKeys(sendOptions, options),
176
+ };
177
+ }
178
+ //# sourceMappingURL=tmux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux.js","sourceRoot":"","sources":["../../../../src/hooks/extensibility/sdk/tmux.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/hooks/extensibility/sdk.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EAGd,MAAM,YAAY,CAAC;AAEpB,UAAU,oBAAoB;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,iBAAiB,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AA6ND,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CA+DhF;AAED,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzF"}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/hooks/extensibility/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOnE,UAAU,oBAAoB;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,iBAAiB,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAYhF;AAED,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzF"}
@@ -1,253 +1,21 @@
1
- import { existsSync } from 'fs';
2
- import { appendFile, mkdir, readFile, unlink, writeFile } from 'fs/promises';
3
- import { createHash } from 'crypto';
4
- import { dirname, join } from 'path';
5
- import { spawnSync } from 'child_process';
6
- import { sleepSync } from '../../utils/sleep.js';
7
- const INJECTION_MARKER = '[OMX_TMUX_INJECT]';
8
- const DEFAULT_COOLDOWN_MS = 15_000;
9
- const DEFAULT_DEDUPE_WINDOW_MS = 60_000;
10
- function asPositiveNumber(value, fallback) {
11
- if (!value)
12
- return fallback;
13
- const parsed = Number(value);
14
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
15
- }
16
- function sanitizePluginName(name) {
17
- const cleaned = (name || 'unknown-plugin').replace(/[^a-zA-Z0-9._-]/g, '-');
18
- return cleaned || 'unknown-plugin';
19
- }
20
- function pluginRootDir(cwd, pluginName) {
21
- return join(cwd, '.omx', 'state', 'hooks', 'plugins', sanitizePluginName(pluginName));
22
- }
23
- function pluginTmuxStatePath(cwd, pluginName) {
24
- return join(pluginRootDir(cwd, pluginName), 'tmux.json');
25
- }
26
- function pluginDataPath(cwd, pluginName) {
27
- return join(pluginRootDir(cwd, pluginName), 'data.json');
28
- }
29
- function pluginLogPath(cwd) {
30
- const day = new Date().toISOString().slice(0, 10);
31
- return join(cwd, '.omx', 'logs', `hooks-${day}.jsonl`);
32
- }
33
- async function appendPluginLog(cwd, pluginName, level, message, meta) {
34
- const logPath = pluginLogPath(cwd);
35
- await mkdir(dirname(logPath), { recursive: true });
36
- await appendFile(logPath, `${JSON.stringify({
37
- timestamp: new Date().toISOString(),
38
- type: 'hook_plugin_log',
39
- plugin: pluginName,
40
- level,
41
- message,
42
- ...meta,
43
- })}\n`).catch(() => { });
44
- }
45
- async function readJsonIfExists(path, fallback) {
46
- if (!existsSync(path))
47
- return fallback;
48
- try {
49
- return JSON.parse(await readFile(path, 'utf-8'));
50
- }
51
- catch {
52
- return fallback;
53
- }
54
- }
55
- function hashDedupeKey(target, text) {
56
- return createHash('sha256').update(`${target}|${text}`).digest('hex');
57
- }
58
- function sleepFractionalSeconds(seconds) {
59
- if (!Number.isFinite(seconds) || seconds <= 0)
60
- return;
61
- sleepSync(Math.round(seconds * 1000));
62
- }
63
- function runTmux(args) {
64
- const result = spawnSync('tmux', args, { encoding: 'utf-8' });
65
- if (result.error)
66
- return { ok: false, stderr: result.error.message };
67
- if (result.status !== 0) {
68
- return { ok: false, stderr: (result.stderr || '').trim() || `tmux exited ${result.status}` };
69
- }
70
- return { ok: true, stdout: (result.stdout || '').trim() };
71
- }
72
- function resolveTmuxTarget(options) {
73
- const paneId = typeof options.paneId === 'string' ? options.paneId.trim() : '';
74
- if (paneId) {
75
- const pane = runTmux(['display-message', '-p', '-t', paneId, '#{pane_id}']);
76
- if (pane.ok && pane.stdout)
77
- return { ok: true, reason: 'ok', target: pane.stdout, paneId: pane.stdout };
78
- return { ok: false, reason: 'target_missing', error: pane.ok ? 'pane_not_found' : pane.stderr };
79
- }
80
- const sessionName = typeof options.sessionName === 'string' ? options.sessionName.trim() : '';
81
- if (sessionName) {
82
- const paneList = runTmux(['list-panes', '-t', sessionName, '-F', '#{pane_id} #{pane_active}']);
83
- if (!paneList.ok) {
84
- return { ok: false, reason: 'target_missing', error: paneList.stderr };
85
- }
86
- const lines = paneList.stdout.split('\n').map((line) => line.trim()).filter(Boolean);
87
- const active = lines.find((line) => line.endsWith(' 1')) || lines[0];
88
- if (!active)
89
- return { ok: false, reason: 'target_missing' };
90
- const resolved = active.split(' ')[0];
91
- return resolved ? { ok: true, reason: 'ok', target: resolved, paneId: resolved } : { ok: false, reason: 'target_missing' };
92
- }
93
- const envPane = typeof process.env.TMUX_PANE === 'string' ? process.env.TMUX_PANE.trim() : '';
94
- if (envPane)
95
- return { ok: true, reason: 'ok', target: envPane, paneId: envPane };
96
- return { ok: false, reason: 'target_missing' };
97
- }
98
- async function sendTmuxKeys(options, context) {
99
- if (!context.sideEffectsEnabled) {
100
- return { ok: false, reason: 'side_effects_disabled' };
101
- }
102
- const text = typeof options.text === 'string' ? options.text : '';
103
- if (!text.trim()) {
104
- return { ok: false, reason: 'invalid_text' };
105
- }
106
- const marker = process.env.OMX_HOOK_PLUGIN_LOOP_MARKER || '[OMX_HOOK_PLUGIN]';
107
- if (marker && text.includes(marker)) {
108
- return { ok: false, reason: 'loop_guard_input_marker' };
109
- }
110
- const targetResolution = resolveTmuxTarget(options);
111
- if (!targetResolution.ok || !targetResolution.target) {
112
- return targetResolution;
113
- }
114
- const tmuxStatePath = pluginTmuxStatePath(context.cwd, context.pluginName);
115
- await mkdir(dirname(tmuxStatePath), { recursive: true });
116
- const tmuxState = await readJsonIfExists(tmuxStatePath, {
117
- last_sent_at: 0,
118
- recent_keys: {},
119
- });
120
- const now = Date.now();
121
- const cooldownMs = typeof options.cooldownMs === 'number'
122
- ? Math.max(0, options.cooldownMs)
123
- : asPositiveNumber(process.env.OMX_HOOK_PLUGIN_COOLDOWN_MS, DEFAULT_COOLDOWN_MS);
124
- const dedupeWindowMs = asPositiveNumber(process.env.OMX_HOOK_PLUGIN_DEDUPE_MS, DEFAULT_DEDUPE_WINDOW_MS);
125
- const minTs = now - dedupeWindowMs;
126
- tmuxState.recent_keys = Object.fromEntries(Object.entries(tmuxState.recent_keys || {}).filter(([, ts]) => Number.isFinite(ts) && ts >= minTs));
127
- if (cooldownMs > 0 && now - (tmuxState.last_sent_at || 0) < cooldownMs) {
128
- return { ok: false, reason: 'cooldown_active', target: targetResolution.target, paneId: targetResolution.target };
129
- }
130
- const dedupeKey = hashDedupeKey(targetResolution.target, text);
131
- if (tmuxState.recent_keys[dedupeKey]) {
132
- return { ok: false, reason: 'duplicate_event', target: targetResolution.target, paneId: targetResolution.target };
133
- }
134
- const markedText = `${text} ${INJECTION_MARKER}`;
135
- const typed = runTmux(['send-keys', '-t', targetResolution.target, '-l', markedText]);
136
- if (!typed.ok) {
137
- return {
138
- ok: false,
139
- reason: 'tmux_failed',
140
- target: targetResolution.target,
141
- paneId: targetResolution.target,
142
- error: typed.stderr,
143
- };
144
- }
145
- if (options.submit !== false) {
146
- // Codex CLI's alternate-screen TUI can swallow an immediate C-m that lands
147
- // in the same tmux send-keys burst as literal text. Give the raw-mode input
148
- // buffer a brief moment to settle before the first submit.
149
- sleepFractionalSeconds(0.12);
150
- const submitA = runTmux(['send-keys', '-t', targetResolution.target, 'C-m']);
151
- sleepFractionalSeconds(0.1);
152
- const submitB = runTmux(['send-keys', '-t', targetResolution.target, 'C-m']);
153
- if (!submitA.ok && !submitB.ok) {
154
- return {
155
- ok: false,
156
- reason: 'tmux_failed',
157
- target: targetResolution.target,
158
- paneId: targetResolution.target,
159
- error: submitA.stderr || submitB.stderr,
160
- };
161
- }
162
- }
163
- tmuxState.last_sent_at = now;
164
- tmuxState.recent_keys[dedupeKey] = now;
165
- await writeFile(tmuxStatePath, JSON.stringify(tmuxState, null, 2));
166
- await appendPluginLog(context.cwd, context.pluginName, 'info', 'tmux.sendKeys', {
167
- hook_event: context.event.event,
168
- target: targetResolution.target,
169
- submitted: options.submit !== false,
170
- }).catch(() => { });
171
- return {
172
- ok: true,
173
- reason: 'ok',
174
- target: targetResolution.target,
175
- paneId: targetResolution.target,
176
- };
177
- }
178
- function normalizeStateKey(key) {
179
- const trimmed = key.trim();
180
- if (!trimmed)
181
- throw new Error('state key is required');
182
- if (trimmed.includes('..') || trimmed.startsWith('/')) {
183
- throw new Error('invalid state key');
184
- }
185
- return trimmed;
186
- }
1
+ import { createHookPluginLogger } from './sdk/logging.js';
2
+ import { clearHookPluginStateFiles, createHookPluginStateApi } from './sdk/plugin-state.js';
3
+ import { sanitizeHookPluginName } from './sdk/paths.js';
4
+ import { createHookPluginOmxApi } from './sdk/runtime-state.js';
5
+ import { createHookPluginTmuxApi } from './sdk/tmux.js';
187
6
  export function createHookPluginSdk(options) {
188
- const pluginName = sanitizePluginName(options.pluginName);
189
- const dataPath = pluginDataPath(options.cwd, pluginName);
190
- async function readData() {
191
- return readJsonIfExists(dataPath, {});
192
- }
193
- async function writeData(value) {
194
- await mkdir(dirname(dataPath), { recursive: true });
195
- await writeFile(dataPath, JSON.stringify(value, null, 2));
196
- }
197
- const logger = {
198
- info: (message, meta = {}) => appendPluginLog(options.cwd, pluginName, 'info', message, {
199
- hook_event: options.event.event,
200
- ...meta,
201
- }),
202
- warn: (message, meta = {}) => appendPluginLog(options.cwd, pluginName, 'warn', message, {
203
- hook_event: options.event.event,
204
- ...meta,
205
- }),
206
- error: (message, meta = {}) => appendPluginLog(options.cwd, pluginName, 'error', message, {
207
- hook_event: options.event.event,
208
- ...meta,
209
- }),
210
- };
7
+ const pluginName = sanitizeHookPluginName(options.pluginName);
211
8
  return {
212
- tmux: {
213
- sendKeys: (sendOptions) => sendTmuxKeys(sendOptions, {
214
- ...options,
215
- pluginName,
216
- }),
217
- },
218
- log: logger,
219
- state: {
220
- read: async (key, fallback) => {
221
- const safeKey = normalizeStateKey(key);
222
- const data = await readData();
223
- if (!(safeKey in data))
224
- return fallback;
225
- return data[safeKey];
226
- },
227
- write: async (key, value) => {
228
- const safeKey = normalizeStateKey(key);
229
- const data = await readData();
230
- data[safeKey] = value;
231
- await writeData(data);
232
- },
233
- delete: async (key) => {
234
- const safeKey = normalizeStateKey(key);
235
- const data = await readData();
236
- if (safeKey in data) {
237
- delete data[safeKey];
238
- await writeData(data);
239
- }
240
- },
241
- all: async () => {
242
- const data = await readData();
243
- return data;
244
- },
245
- },
9
+ tmux: createHookPluginTmuxApi({
10
+ ...options,
11
+ pluginName,
12
+ }),
13
+ log: createHookPluginLogger(options.cwd, pluginName, options.event),
14
+ state: createHookPluginStateApi(options.cwd, pluginName),
15
+ omx: createHookPluginOmxApi(options.cwd),
246
16
  };
247
17
  }
248
18
  export async function clearHookPluginState(cwd, pluginName) {
249
- const root = pluginRootDir(cwd, pluginName);
250
- await unlink(join(root, 'data.json')).catch(() => { });
251
- await unlink(join(root, 'tmux.json')).catch(() => { });
19
+ await clearHookPluginStateFiles(cwd, pluginName);
252
20
  }
253
21
  //# sourceMappingURL=sdk.js.map