shennian 0.3.1 → 0.3.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.
Files changed (296) hide show
  1. package/README.md +3 -1
  2. package/dist/publish-build-manifest.json +291 -341
  3. package/dist/src/agents/codex.js +1 -1
  4. package/dist/src/agents/command-spec.js +2 -2
  5. package/dist/src/agents/detect.js +1 -1
  6. package/dist/src/agents/model-registry/discovery.js +2 -2
  7. package/dist/src/agents/model-registry/parsers.d.ts +1 -0
  8. package/dist/src/agents/model-registry/parsers.js +4 -4
  9. package/dist/src/agents/model-registry/service.js +1 -1
  10. package/dist/src/agents/native-capabilities.d.ts +10 -0
  11. package/dist/src/agents/native-capabilities.js +1 -0
  12. package/dist/src/agents/platform-instructions.js +5 -5
  13. package/dist/src/agents/runtime-compatibility.d.ts +11 -0
  14. package/dist/src/agents/runtime-compatibility.js +1 -0
  15. package/dist/src/agents/workbuddy.d.ts +44 -0
  16. package/dist/src/agents/workbuddy.js +3 -0
  17. package/dist/src/commands/daemon.d.ts +1 -0
  18. package/dist/src/commands/daemon.js +9 -9
  19. package/dist/src/commands/integration.d.ts +2 -0
  20. package/dist/src/commands/integration.js +1 -0
  21. package/dist/src/commands/manager.js +1 -2
  22. package/dist/src/commands/pair-browser.d.ts +43 -0
  23. package/dist/src/commands/pair-browser.js +1 -0
  24. package/dist/src/commands/pair.d.ts +2 -0
  25. package/dist/src/commands/pair.js +11 -8
  26. package/dist/src/commands/room.d.ts +13 -0
  27. package/dist/src/commands/room.js +4 -0
  28. package/dist/src/commands/upgrade.d.ts +3 -0
  29. package/dist/src/commands/upgrade.js +1 -1
  30. package/dist/src/config/index.d.ts +3 -1
  31. package/dist/src/config/index.js +2 -1
  32. package/dist/src/daemon-ipc/client.d.ts +8 -0
  33. package/dist/src/daemon-ipc/client.js +3 -0
  34. package/dist/src/daemon-ipc/paths.d.ts +7 -0
  35. package/dist/src/daemon-ipc/paths.js +1 -0
  36. package/dist/src/daemon-ipc/protocol.d.ts +30 -0
  37. package/dist/src/daemon-ipc/protocol.js +1 -0
  38. package/dist/src/daemon-ipc/secret.d.ts +3 -0
  39. package/dist/src/daemon-ipc/secret.js +3 -0
  40. package/dist/src/daemon-ipc/server.d.ts +13 -0
  41. package/dist/src/daemon-ipc/server.js +3 -0
  42. package/dist/src/index.js +2 -2
  43. package/dist/src/integrations/claude-room-plugin-lifecycle.d.ts +47 -0
  44. package/dist/src/integrations/claude-room-plugin-lifecycle.js +1 -0
  45. package/dist/src/integrations/codex-room-plugin-lifecycle.d.ts +49 -0
  46. package/dist/src/integrations/codex-room-plugin-lifecycle.js +1 -0
  47. package/dist/src/integrations/room-plugin-bundles.d.ts +23 -0
  48. package/dist/src/integrations/room-plugin-bundles.js +1 -0
  49. package/dist/src/integrations/room-plugin-lifecycle-common.d.ts +17 -0
  50. package/dist/src/integrations/room-plugin-lifecycle-common.js +1 -0
  51. package/dist/src/integrations/room-plugin-shared.d.ts +24 -0
  52. package/dist/src/integrations/room-plugin-shared.js +2 -0
  53. package/dist/src/integrations/room-session-hook.d.ts +30 -0
  54. package/dist/src/integrations/room-session-hook.js +2 -0
  55. package/dist/src/integrations/workbuddy-plugin-bundle.d.ts +15 -0
  56. package/dist/src/integrations/workbuddy-plugin-bundle.js +1 -0
  57. package/dist/src/integrations/workbuddy-plugin-lifecycle.d.ts +52 -0
  58. package/dist/src/integrations/workbuddy-plugin-lifecycle.js +2 -0
  59. package/dist/src/integrations/workbuddy-session-hook.d.ts +22 -0
  60. package/dist/src/integrations/workbuddy-session-hook.js +1 -0
  61. package/dist/src/manager/prompt.d.ts +1 -1
  62. package/dist/src/manager/prompt.js +1 -1
  63. package/dist/src/manager/retired-external-channel-compat.d.ts +78 -0
  64. package/dist/src/manager/retired-external-channel-compat.js +1 -0
  65. package/dist/src/manager/runtime.d.ts +1 -3
  66. package/dist/src/manager/runtime.js +7 -7
  67. package/dist/src/relay/client.d.ts +2 -0
  68. package/dist/src/relay/client.js +1 -1
  69. package/dist/src/room/activation-audit.d.ts +15 -0
  70. package/dist/src/room/activation-audit.js +2 -0
  71. package/dist/src/room/activation-policy.d.ts +8 -0
  72. package/dist/src/room/activation-policy.js +1 -0
  73. package/dist/src/room/activation-service.d.ts +25 -0
  74. package/dist/src/room/activation-service.js +2 -0
  75. package/dist/src/room/agent-tool.d.ts +62 -0
  76. package/dist/src/room/agent-tool.js +1 -0
  77. package/dist/src/room/attachment-cache.d.ts +40 -0
  78. package/dist/src/room/attachment-cache.js +2 -0
  79. package/dist/src/room/attachment-upload.d.ts +20 -0
  80. package/dist/src/room/attachment-upload.js +1 -0
  81. package/dist/src/room/authorized-action-coordinator.d.ts +40 -0
  82. package/dist/src/room/authorized-action-coordinator.js +1 -0
  83. package/dist/src/room/authorized-action-ipc.d.ts +11 -0
  84. package/dist/src/room/authorized-action-ipc.js +1 -0
  85. package/dist/src/room/device-authorization-client.d.ts +49 -0
  86. package/dist/src/room/device-authorization-client.js +1 -0
  87. package/dist/src/room/host-session-observation-ipc.d.ts +9 -0
  88. package/dist/src/room/host-session-observation-ipc.js +1 -0
  89. package/dist/src/room/host-session-observations.d.ts +54 -0
  90. package/dist/src/room/host-session-observations.js +2 -0
  91. package/dist/src/room/invite-bootstrap.d.ts +35 -0
  92. package/dist/src/room/invite-bootstrap.js +1 -0
  93. package/dist/src/room/invocation-token-ipc.d.ts +9 -0
  94. package/dist/src/room/invocation-token-ipc.js +1 -0
  95. package/dist/src/room/invocation-token.d.ts +53 -0
  96. package/dist/src/room/invocation-token.js +1 -0
  97. package/dist/src/room/jsonl-mirror.d.ts +73 -0
  98. package/dist/src/room/jsonl-mirror.js +6 -0
  99. package/dist/src/room/managed-room-binding.d.ts +42 -0
  100. package/dist/src/room/managed-room-binding.js +1 -0
  101. package/dist/src/room/managed-room-session.d.ts +143 -0
  102. package/dist/src/room/managed-room-session.js +3 -0
  103. package/dist/src/room/mirror-sync-service.d.ts +33 -0
  104. package/dist/src/room/mirror-sync-service.js +1 -0
  105. package/dist/src/room/operator-ipc.d.ts +11 -0
  106. package/dist/src/room/operator-ipc.js +1 -0
  107. package/dist/src/room/pending-authorized-actions.d.ts +69 -0
  108. package/dist/src/room/pending-authorized-actions.js +3 -0
  109. package/dist/src/room/room-action-client.d.ts +210 -0
  110. package/dist/src/room/room-action-client.js +1 -0
  111. package/dist/src/room/room-action-ipc.d.ts +33 -0
  112. package/dist/src/room/room-action-ipc.js +1 -0
  113. package/dist/src/room/room-action-schema.d.ts +3 -0
  114. package/dist/src/room/room-action-schema.js +1 -0
  115. package/dist/src/room/room-authorized-action-executor.d.ts +23 -0
  116. package/dist/src/room/room-authorized-action-executor.js +1 -0
  117. package/dist/src/room/room-binding-client.d.ts +51 -0
  118. package/dist/src/room/room-binding-client.js +1 -0
  119. package/dist/src/room/room-binding-ipc.d.ts +29 -0
  120. package/dist/src/room/room-binding-ipc.js +1 -0
  121. package/dist/src/room/session-registry-ipc.d.ts +14 -0
  122. package/dist/src/room/session-registry-ipc.js +1 -0
  123. package/dist/src/room/session-registry.d.ts +128 -0
  124. package/dist/src/room/session-registry.js +2 -0
  125. package/dist/src/room/tool-schema.d.ts +118 -0
  126. package/dist/src/room/tool-schema.js +1 -0
  127. package/dist/src/room/tool-server.d.ts +5 -0
  128. package/dist/src/room/tool-server.js +3 -0
  129. package/dist/src/session/handlers/agent-capabilities.d.ts +3 -0
  130. package/dist/src/session/handlers/agent-capabilities.js +1 -0
  131. package/dist/src/session/handlers/agent-config.js +1 -1
  132. package/dist/src/session/handlers/chat.js +2 -2
  133. package/dist/src/session/handlers/control.js +1 -1
  134. package/dist/src/session/handlers/fs.d.ts +1 -0
  135. package/dist/src/session/handlers/fs.js +1 -1
  136. package/dist/src/session/handlers/room-managed.d.ts +4 -0
  137. package/dist/src/session/handlers/room-managed.js +1 -0
  138. package/dist/src/session/manager.d.ts +18 -6
  139. package/dist/src/session/manager.js +1 -1
  140. package/dist/src/session/types.d.ts +5 -0
  141. package/dist/src/upgrade/engine.d.ts +14 -1
  142. package/dist/src/upgrade/engine.js +2 -2
  143. package/dist/src/upgrade/scheduler.d.ts +45 -0
  144. package/dist/src/upgrade/scheduler.js +1 -0
  145. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts +17 -0
  146. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts.map +1 -0
  147. package/node_modules/@shennian/wire/dist/agent-capabilities.js +2 -0
  148. package/node_modules/@shennian/wire/dist/agent-capabilities.js.map +1 -0
  149. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +41 -0
  150. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts.map +1 -0
  151. package/node_modules/@shennian/wire/dist/agent-workspace.js +3 -0
  152. package/node_modules/@shennian/wire/dist/agent-workspace.js.map +1 -0
  153. package/node_modules/@shennian/wire/dist/index.d.ts +3 -1
  154. package/node_modules/@shennian/wire/dist/index.d.ts.map +1 -1
  155. package/node_modules/@shennian/wire/dist/index.js +3 -1
  156. package/node_modules/@shennian/wire/dist/index.js.map +1 -1
  157. package/node_modules/@shennian/wire/dist/machine.d.ts +1 -0
  158. package/node_modules/@shennian/wire/dist/machine.d.ts.map +1 -1
  159. package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
  160. package/node_modules/@shennian/wire/dist/methods.d.ts.map +1 -1
  161. package/node_modules/@shennian/wire/dist/room.d.ts +112 -0
  162. package/node_modules/@shennian/wire/dist/room.d.ts.map +1 -0
  163. package/node_modules/@shennian/wire/dist/room.js +4 -0
  164. package/node_modules/@shennian/wire/dist/room.js.map +1 -0
  165. package/node_modules/@shennian/wire/dist/session.d.ts +15 -2
  166. package/node_modules/@shennian/wire/dist/session.d.ts.map +1 -1
  167. package/node_modules/@shennian/wire/dist/session.js +1 -1
  168. package/node_modules/@shennian/wire/dist/session.js.map +1 -1
  169. package/package.json +3 -5
  170. package/dist/scripts/wechat-rpa-confirmation.mjs +0 -5
  171. package/dist/src/agents/pi-context.d.ts +0 -42
  172. package/dist/src/agents/pi-context.js +0 -12
  173. package/dist/src/agents/pi.d.ts +0 -70
  174. package/dist/src/agents/pi.js +0 -17
  175. package/dist/src/channels/base.d.ts +0 -144
  176. package/dist/src/channels/base.js +0 -1
  177. package/dist/src/channels/registry.d.ts +0 -8
  178. package/dist/src/channels/registry.js +0 -1
  179. package/dist/src/channels/reply-split.d.ts +0 -1
  180. package/dist/src/channels/reply-split.js +0 -10
  181. package/dist/src/channels/runtime.d.ts +0 -138
  182. package/dist/src/channels/runtime.js +0 -5
  183. package/dist/src/channels/secret-registry.d.ts +0 -35
  184. package/dist/src/channels/secret-registry.js +0 -1
  185. package/dist/src/channels/websocket.d.ts +0 -55
  186. package/dist/src/channels/websocket.js +0 -8
  187. package/dist/src/channels/wechat-channel/anchor.d.ts +0 -10
  188. package/dist/src/channels/wechat-channel/anchor.js +0 -1
  189. package/dist/src/channels/wechat-channel/automation-lane.d.ts +0 -35
  190. package/dist/src/channels/wechat-channel/automation-lane.js +0 -2
  191. package/dist/src/channels/wechat-channel/client.d.ts +0 -283
  192. package/dist/src/channels/wechat-channel/client.js +0 -1
  193. package/dist/src/channels/wechat-channel/cooldown.d.ts +0 -21
  194. package/dist/src/channels/wechat-channel/cooldown.js +0 -1
  195. package/dist/src/channels/wechat-channel/core/fixture-compare.d.ts +0 -52
  196. package/dist/src/channels/wechat-channel/core/fixture-compare.js +0 -1
  197. package/dist/src/channels/wechat-channel/core/media-action-plan.d.ts +0 -3
  198. package/dist/src/channels/wechat-channel/core/media-action-plan.js +0 -1
  199. package/dist/src/channels/wechat-channel/core/schema.d.ts +0 -67
  200. package/dist/src/channels/wechat-channel/core/schema.js +0 -1
  201. package/dist/src/channels/wechat-channel/core/trace-comparator.d.ts +0 -28
  202. package/dist/src/channels/wechat-channel/core/trace-comparator.js +0 -1
  203. package/dist/src/channels/wechat-channel/fingerprint.d.ts +0 -31
  204. package/dist/src/channels/wechat-channel/fingerprint.js +0 -1
  205. package/dist/src/channels/wechat-channel/helper-assets.d.ts +0 -103
  206. package/dist/src/channels/wechat-channel/helper-assets.js +0 -1
  207. package/dist/src/channels/wechat-channel/helper-client.d.ts +0 -87
  208. package/dist/src/channels/wechat-channel/helper-client.js +0 -3
  209. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +0 -122
  210. package/dist/src/channels/wechat-channel/helper-protocol.js +0 -1
  211. package/dist/src/channels/wechat-channel/human-coordination.d.ts +0 -25
  212. package/dist/src/channels/wechat-channel/human-coordination.js +0 -1
  213. package/dist/src/channels/wechat-channel/index.d.ts +0 -21
  214. package/dist/src/channels/wechat-channel/index.js +0 -1
  215. package/dist/src/channels/wechat-channel/ledger.d.ts +0 -48
  216. package/dist/src/channels/wechat-channel/ledger.js +0 -2
  217. package/dist/src/channels/wechat-channel/media-cache-resolver.d.ts +0 -42
  218. package/dist/src/channels/wechat-channel/media-cache-resolver.js +0 -1
  219. package/dist/src/channels/wechat-channel/media-resolver.d.ts +0 -186
  220. package/dist/src/channels/wechat-channel/media-resolver.js +0 -1
  221. package/dist/src/channels/wechat-channel/message-key.d.ts +0 -19
  222. package/dist/src/channels/wechat-channel/message-key.js +0 -1
  223. package/dist/src/channels/wechat-channel/observer.d.ts +0 -171
  224. package/dist/src/channels/wechat-channel/observer.js +0 -7
  225. package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +0 -96
  226. package/dist/src/channels/wechat-channel/outbound-ledger.js +0 -2
  227. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +0 -74
  228. package/dist/src/channels/wechat-channel/outbound-sender.js +0 -1
  229. package/dist/src/channels/wechat-channel/pacing.d.ts +0 -3
  230. package/dist/src/channels/wechat-channel/pacing.js +0 -1
  231. package/dist/src/channels/wechat-channel/preflight.d.ts +0 -46
  232. package/dist/src/channels/wechat-channel/preflight.js +0 -1
  233. package/dist/src/channels/wechat-channel/runner.d.ts +0 -43
  234. package/dist/src/channels/wechat-channel/runner.js +0 -1
  235. package/dist/src/channels/wechat-channel/runtime.d.ts +0 -50
  236. package/dist/src/channels/wechat-channel/runtime.js +0 -1
  237. package/dist/src/channels/wechat-channel/scheduler.d.ts +0 -74
  238. package/dist/src/channels/wechat-channel/scheduler.js +0 -1
  239. package/dist/src/channels/wechat-channel/vector-store.d.ts +0 -44
  240. package/dist/src/channels/wechat-channel/vector-store.js +0 -1
  241. package/dist/src/channels/wechat-rpa/macos-flow.d.ts +0 -55
  242. package/dist/src/channels/wechat-rpa/macos-flow.js +0 -1
  243. package/dist/src/channels/wechat-rpa/macos.d.ts +0 -11
  244. package/dist/src/channels/wechat-rpa/macos.js +0 -21
  245. package/dist/src/channels/wechat-rpa/normalizer.d.ts +0 -49
  246. package/dist/src/channels/wechat-rpa/normalizer.js +0 -7
  247. package/dist/src/channels/wechat-rpa/product-channel.d.ts +0 -76
  248. package/dist/src/channels/wechat-rpa/product-channel.js +0 -16
  249. package/dist/src/channels/wechat-rpa-session-sync.d.ts +0 -26
  250. package/dist/src/channels/wechat-rpa-session-sync.js +0 -1
  251. package/dist/src/channels/wechat-rpa.d.ts +0 -104
  252. package/dist/src/channels/wechat-rpa.js +0 -6
  253. package/dist/src/channels/wecom.d.ts +0 -51
  254. package/dist/src/channels/wecom.js +0 -4
  255. package/dist/src/commands/external-attachments.d.ts +0 -9
  256. package/dist/src/commands/external-attachments.js +0 -1
  257. package/dist/src/commands/external.d.ts +0 -2
  258. package/dist/src/commands/external.js +0 -1
  259. package/dist/src/commands/helper-runtime-official-download.d.ts +0 -80
  260. package/dist/src/commands/helper-runtime-official-download.js +0 -1
  261. package/dist/src/commands/runtime.d.ts +0 -120
  262. package/dist/src/commands/runtime.js +0 -1
  263. package/dist/src/commands/wechat/command.d.ts +0 -3
  264. package/dist/src/commands/wechat/command.js +0 -1
  265. package/dist/src/commands/wechat/direct.d.ts +0 -14
  266. package/dist/src/commands/wechat/direct.js +0 -2
  267. package/dist/src/commands/wechat/doctor.d.ts +0 -2
  268. package/dist/src/commands/wechat/doctor.js +0 -1
  269. package/dist/src/commands/wechat/ipc.d.ts +0 -9
  270. package/dist/src/commands/wechat/ipc.js +0 -1
  271. package/dist/src/commands/wechat/operations.d.ts +0 -3
  272. package/dist/src/commands/wechat/operations.js +0 -4
  273. package/dist/src/commands/wechat/output.d.ts +0 -7
  274. package/dist/src/commands/wechat/output.js +0 -4
  275. package/dist/src/commands/wechat/types.d.ts +0 -142
  276. package/dist/src/commands/wechat/types.js +0 -1
  277. package/dist/src/commands/wechat/utils.d.ts +0 -16
  278. package/dist/src/commands/wechat/utils.js +0 -1
  279. package/dist/src/commands/wechat.d.ts +0 -6
  280. package/dist/src/commands/wechat.js +0 -1
  281. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +0 -105
  282. package/dist/src/devtools/wechat-channel-action-smoke.js +0 -10
  283. package/dist/src/devtools/wechat-channel-download-report.d.ts +0 -111
  284. package/dist/src/devtools/wechat-channel-download-report.js +0 -2
  285. package/dist/src/devtools/wechat-channel-markdown-report.d.ts +0 -6
  286. package/dist/src/devtools/wechat-channel-markdown-report.js +0 -5
  287. package/dist/src/devtools/wechat-channel-product-steps.d.ts +0 -9
  288. package/dist/src/devtools/wechat-channel-product-steps.js +0 -40
  289. package/dist/src/session/handlers/skills.d.ts +0 -7
  290. package/dist/src/session/handlers/skills.js +0 -1
  291. package/dist/src/session/handlers/wechat-helper-runtime.d.ts +0 -33
  292. package/dist/src/session/handlers/wechat-helper-runtime.js +0 -1
  293. package/dist/src/skills/registry.d.ts +0 -16
  294. package/dist/src/skills/registry.js +0 -15
  295. package/dist/src/skills/setup.d.ts +0 -11
  296. package/dist/src/skills/setup.js +0 -1
@@ -0,0 +1,73 @@
1
+ import type { RoomMessageView } from '@shennian/wire';
2
+ type MirrorState = {
3
+ schemaVersion: 1;
4
+ roomId: string;
5
+ lastSyncedSequence: string;
6
+ messageCount: number;
7
+ lastError: string | null;
8
+ updatedAt: string;
9
+ };
10
+ export type RoomMirrorEvent = {
11
+ schemaVersion: 1;
12
+ roomId: string;
13
+ sequence: string;
14
+ messageId: string;
15
+ kind: RoomMessageView['kind'];
16
+ author: {
17
+ type: RoomMessageView['author']['principalType'];
18
+ membershipId: string;
19
+ ownerUserId: string;
20
+ displayName: string;
21
+ ownerDisplayName: string | null;
22
+ };
23
+ mentions: NonNullable<RoomMessageView['mentions']>;
24
+ text: string | null;
25
+ replyToMessageId: string | null;
26
+ attachments: RoomMessageView['attachments'];
27
+ createdAt: string;
28
+ editedAt: string | null;
29
+ deletedAt: string | null;
30
+ activationCause: RoomMessageView['activationCause'];
31
+ };
32
+ export declare class RoomJsonlMirrorError extends Error {
33
+ readonly code: string;
34
+ constructor(code: string, message: string);
35
+ }
36
+ export declare class RoomJsonlMirror {
37
+ readonly roomId: string;
38
+ readonly directory: string;
39
+ readonly metadataPath: string;
40
+ readonly messagesPath: string;
41
+ readonly statePath: string;
42
+ readonly attachmentsDirectory: string;
43
+ constructor(roomId: string, rootDirectory?: string);
44
+ append(input: {
45
+ messages: RoomMessageView[];
46
+ historyFromSequence: string;
47
+ roomName?: string | null;
48
+ now?: Date;
49
+ }): {
50
+ appended: number;
51
+ lastSyncedSequence: string;
52
+ };
53
+ rebuild(input: {
54
+ messages: RoomMessageView[];
55
+ historyFromSequence: string;
56
+ roomName?: string | null;
57
+ now?: Date;
58
+ }): MirrorState;
59
+ markAccessRevoked(now?: Date): void;
60
+ recordSyncError(code: string, now?: Date): void;
61
+ readState(): MirrorState;
62
+ search(input: {
63
+ query: string;
64
+ limit?: number;
65
+ }): RoomMirrorEvent[];
66
+ private ensureLayout;
67
+ private recoverPartialLine;
68
+ private reconcileStateFromMessages;
69
+ private readMetadata;
70
+ private writeMetadata;
71
+ private writeState;
72
+ }
73
+ export {};
@@ -0,0 +1,6 @@
1
+ import p from"node:crypto";import n from"node:fs";import l from"node:path";import{StringDecoder as L}from"node:string_decoder";import{resolveShennianPath as k}from"../config/index.js";const C=/^[A-Za-z0-9_.:@-]{1,191}$/,w=2*1024*1024;class a extends Error{code;constructor(e,r){super(r),this.code=e,this.name="RoomJsonlMirrorError"}}class z{roomId;directory;metadataPath;messagesPath;statePath;attachmentsDirectory;constructor(e,r=k("rooms")){this.roomId=e,b(e,"room_id"),this.directory=l.join(r,e),this.metadataPath=l.join(this.directory,"metadata.json"),this.messagesPath=l.join(this.directory,"messages.jsonl"),this.statePath=l.join(this.directory,"state.json"),this.attachmentsDirectory=l.join(this.directory,"attachments"),this.ensureLayout(),this.recoverPartialLine(),this.reconcileStateFromMessages()}append(e){const r=u(e.historyFromSequence),o=this.readState(),i=BigInt(o.lastSyncedSequence),s=[];let c=i;for(const O of e.messages){const I=q(O,this.roomId),f=BigInt(I.sequence);if(!(f<=i)){if(f<=c)throw new a("room_mirror_sequence_order","Room mirror messages must be strictly ordered.");s.push(I),c=f}}const m=e.now??new Date;if(this.writeMetadata({schemaVersion:1,roomId:this.roomId,roomName:e.roomName?.trim()||this.readMetadata()?.roomName||null,historyFromSequence:r,access:"active",updatedAt:m.toISOString()}),s.length===0)return{appended:0,lastSyncedSequence:o.lastSyncedSequence};const d=s.map(v).join(""),S=l.join(this.directory,`.messages-batch-${process.pid}-${p.randomBytes(8).toString("hex")}.jsonl`);try{y(S,d,"wx"),_(S,this.roomId),$(this.messagesPath,d)}finally{try{n.unlinkSync(S)}catch{}}const g={schemaVersion:1,roomId:this.roomId,lastSyncedSequence:s.at(-1).sequence,messageCount:o.messageCount+s.length,lastError:null,updatedAt:m.toISOString()};return this.writeState(g),{appended:s.length,lastSyncedSequence:g.lastSyncedSequence}}rebuild(e){u(e.historyFromSequence);const r=e.messages.map(m=>q(m,this.roomId));x(r);const o=r.map(v).join(""),i=l.join(this.directory,`.messages-rebuild-${process.pid}-${p.randomBytes(8).toString("hex")}.jsonl`);try{y(i,o,"wx"),_(i,this.roomId),P(i,this.messagesPath)}finally{try{n.unlinkSync(i)}catch{}}const s=e.now??new Date;this.writeMetadata({schemaVersion:1,roomId:this.roomId,roomName:e.roomName?.trim()||null,historyFromSequence:e.historyFromSequence,access:"active",updatedAt:s.toISOString()});const c={schemaVersion:1,roomId:this.roomId,lastSyncedSequence:r.at(-1)?.sequence??"0",messageCount:r.length,lastError:null,updatedAt:s.toISOString()};return this.writeState(c),c}markAccessRevoked(e=new Date){const r=this.readMetadata();this.writeMetadata({schemaVersion:1,roomId:this.roomId,roomName:r?.roomName??null,historyFromSequence:r?.historyFromSequence??"1",access:"revoked",updatedAt:e.toISOString()})}recordSyncError(e,r=new Date){const o=this.readState();this.writeState({...o,lastError:e.replace(/[^a-z0-9_-]/gi,"_").slice(0,120)||"room_sync_failed",updatedAt:r.toISOString()})}readState(){return F(this.statePath,this.roomId)}search(e){const r=e.query.trim().toLocaleLowerCase(),o=e.limit??20;if(!r||r.length>200)throw new a("invalid_room_search_query","Invalid Room search query.");if(!Number.isInteger(o)||o<1||o>100)throw new a("invalid_room_search_limit","Invalid Room search limit.");const i=[];return R(this.messagesPath,s=>{const c=N(s,this.roomId);c.deletedAt||!D(c).toLocaleLowerCase().includes(r)||(i.push(c),i.length>o&&i.shift())}),i.reverse()}ensureLayout(){A(this.directory),A(this.attachmentsDirectory),n.existsSync(this.messagesPath)||y(this.messagesPath,"","wx")}recoverPartialLine(){const e=h(this.messagesPath);if(e.size===0)return;const r=n.openSync(this.messagesPath,"r+");try{const o=Buffer.alloc(1);if(n.readSync(r,o,0,1,e.size-1),o[0]===10)return;const i=64*1024;let s=e.size;for(;s>0;){const c=Math.min(i,s);s-=c;const m=Buffer.alloc(c);n.readSync(r,m,0,c,s);const d=m.lastIndexOf(10);if(d>=0){n.ftruncateSync(r,s+d+1),n.fsyncSync(r);return}}n.ftruncateSync(r,0),n.fsyncSync(r)}finally{n.closeSync(r)}}reconcileStateFromMessages(){const e=_(this.messagesPath,this.roomId),r=F(this.statePath,this.roomId);(r.lastSyncedSequence!==e.lastSequence||r.messageCount!==e.messageCount)&&this.writeState({...r,lastSyncedSequence:e.lastSequence,messageCount:e.messageCount,updatedAt:new Date().toISOString()})}readMetadata(){if(!n.existsSync(this.metadataPath))return null;h(this.metadataPath);const e=JSON.parse(n.readFileSync(this.metadataPath,"utf8"));if(e.schemaVersion!==1||e.roomId!==this.roomId||e.access!=="active"&&e.access!=="revoked"||typeof e.historyFromSequence!="string")throw new a("corrupt_room_mirror_metadata","Room mirror metadata is corrupt.");return u(e.historyFromSequence),e}writeMetadata(e){M(this.metadataPath,e)}writeState(e){M(this.statePath,e)}}function q(t,e){if(t.roomId!==e)throw new a("room_mirror_room_mismatch","Room mirror message mismatch.");if(b(t.id,"message_id"),u(t.sequence),!t.author||!["human","agent"].includes(t.author.principalType))throw new a("invalid_room_mirror_message","Invalid Room message author.");const r=t.attachments.map(o=>{if(o.downloadRef!==`shennian-room-attachment:${o.attachmentId}`||!/^[a-f0-9]{64}$/.test(o.sha256)||!/^\d+$/.test(o.byteSize))throw new a("invalid_room_mirror_attachment","Invalid Room attachment metadata.");return{...o}});return{schemaVersion:1,roomId:e,sequence:t.sequence,messageId:t.id,kind:t.kind,author:{type:t.author.principalType,membershipId:t.author.membershipId,ownerUserId:t.author.ownerUserId,displayName:t.author.displayName,ownerDisplayName:t.author.ownerDisplayName??null},mentions:[...t.mentions??[]],text:t.text??null,replyToMessageId:t.replyToMessageId??null,attachments:r,createdAt:t.createdAt,editedAt:t.editedAt??null,deletedAt:t.deletedAt??null,activationCause:t.activationCause??null}}function v(t){const e=`${JSON.stringify(t)}
2
+ `;if(Buffer.byteLength(e,"utf8")>w)throw new a("room_mirror_line_too_large","Room mirror event is too large.");return e}function x(t){let e=0n;const r=new Set;for(const o of t){const i=BigInt(o.sequence);if(i<=e||r.has(o.messageId))throw new a("room_mirror_sequence_order","Room mirror messages must be unique and strictly ordered.");e=i,r.add(o.messageId)}}function _(t,e){h(t);const r=[];return R(t,o=>r.push(N(o,e))),x(r),{lastSequence:r.at(-1)?.sequence??"0",messageCount:r.length}}function R(t,e){h(t);const r=n.openSync(t,"r"),o=Buffer.alloc(64*1024),i=new L("utf8");let s="";try{for(;;){const c=n.readSync(r,o,0,o.length,null);if(c===0)break;if(s+=i.write(o.subarray(0,c)),Buffer.byteLength(s,"utf8")>w+o.length)throw new a("room_mirror_line_too_large","Room mirror event is too large.");let m=s.indexOf(`
3
+ `);for(;m>=0;){const d=s.slice(0,m);s=s.slice(m+1),d&&e(d),m=s.indexOf(`
4
+ `)}}if(s+=i.end(),s)throw new a("corrupt_room_mirror","Room mirror has an incomplete line.")}finally{n.closeSync(r)}}function N(t,e){if(Buffer.byteLength(t,"utf8")>w)throw new a("room_mirror_line_too_large","Room mirror event is too large.");let r;try{r=JSON.parse(t)}catch{throw new a("corrupt_room_mirror","Room mirror JSONL is corrupt.")}const o=r;if(o.schemaVersion!==1||o.roomId!==e||typeof o.sequence!="string"||typeof o.messageId!="string")throw new a("corrupt_room_mirror","Room mirror event is invalid.");return u(o.sequence),o}function D(t){return[t.text,t.author.displayName,t.author.ownerDisplayName,...t.mentions.map(e=>e.displayName),...t.attachments.map(e=>e.name)].filter(e=>typeof e=="string").join(`
5
+ `)}function F(t,e){if(!n.existsSync(t))return{schemaVersion:1,roomId:e,lastSyncedSequence:"0",messageCount:0,lastError:null,updatedAt:new Date(0).toISOString()};h(t);const r=JSON.parse(n.readFileSync(t,"utf8"));if(r.schemaVersion!==1||r.roomId!==e||typeof r.lastSyncedSequence!="string"||!Number.isSafeInteger(r.messageCount)||r.messageCount<0)throw new a("corrupt_room_mirror_state","Room mirror state is corrupt.");return u(r.lastSyncedSequence),r}function u(t){if(!/^(0|[1-9]\d{0,19})$/.test(t))throw new a("invalid_room_sequence","Invalid Room sequence.");return t}function b(t,e){if(!C.test(t))throw new a(`invalid_${e}`,`Invalid ${e}.`)}function A(t){if(n.existsSync(t)){const e=n.lstatSync(t);if(!e.isDirectory()||e.isSymbolicLink())throw new a("unsafe_room_mirror_path","Unsafe Room mirror directory.")}else n.mkdirSync(t,{recursive:!0,mode:448});process.platform!=="win32"&&n.chmodSync(t,448)}function h(t){const e=n.lstatSync(t);if(!e.isFile()||e.isSymbolicLink())throw new a("unsafe_room_mirror_path","Unsafe Room mirror file.");return process.platform!=="win32"&&(e.mode&63)!==0&&n.chmodSync(t,384),e}function y(t,e,r){n.writeFileSync(t,e,{encoding:"utf8",mode:384,flag:r}),process.platform!=="win32"&&n.chmodSync(t,384)}function $(t,e){h(t);const r=n.openSync(t,"a");try{n.writeFileSync(r,e,"utf8"),n.fsyncSync(r)}finally{n.closeSync(r)}}function P(t,e){process.platform==="win32"&&n.existsSync(e)&&n.unlinkSync(e),n.renameSync(t,e),process.platform!=="win32"&&n.chmodSync(e,384)}function M(t,e){const r=`${t}.tmp-${process.pid}-${p.randomBytes(8).toString("hex")}`;try{y(r,`${JSON.stringify(e,null,2)}
6
+ `,"wx"),P(r,t)}finally{try{n.unlinkSync(r)}catch{}}}export{z as RoomJsonlMirror,a as RoomJsonlMirrorError};
@@ -0,0 +1,42 @@
1
+ import type { AgentType } from '@shennian/wire';
2
+ import type { HttpRoomBindingClient } from './room-binding-client.js';
3
+ import type { RoomSessionRegistry } from './session-registry.js';
4
+ export type ManagedRoomBindInput = {
5
+ roomId: string;
6
+ agentIdentityId: string;
7
+ nianSessionId: string;
8
+ assignmentIntentId: string;
9
+ agentSessionId: string;
10
+ workDir: string;
11
+ agentType: AgentType;
12
+ };
13
+ export type ManagedRoomBinder = {
14
+ bind(input: ManagedRoomBindInput): Promise<{
15
+ binding: {
16
+ id: string;
17
+ roomId: string;
18
+ agentIdentityId: string;
19
+ agentMembershipId: string;
20
+ nianSessionId: string;
21
+ status: 'pending_approval' | 'active' | 'paused' | 'detached';
22
+ activationMode: 'auto_follow' | 'mention_only' | 'paused';
23
+ };
24
+ idempotent: boolean;
25
+ }>;
26
+ };
27
+ export declare class TrustedManagedRoomBinder implements ManagedRoomBinder {
28
+ private readonly deps;
29
+ constructor(deps: {
30
+ registry: RoomSessionRegistry;
31
+ bindingClient: Pick<HttpRoomBindingClient, 'create'>;
32
+ getMachineCredentials: () => {
33
+ machineId?: string;
34
+ machineToken?: string;
35
+ };
36
+ hostProcessIdentity: () => string;
37
+ });
38
+ bind(input: ManagedRoomBindInput): Promise<{
39
+ binding: import("./room-binding-client.js").RemoteRoomBinding;
40
+ idempotent: boolean;
41
+ }>;
42
+ }
@@ -0,0 +1 @@
1
+ class a{deps;constructor(e){this.deps=e}async bind(e){const{machineId:t,machineToken:d}=this.deps.getMachineCredentials();if(!t||!d)throw new Error("Machine is not paired with Shennian.");const s=r(e.agentType),o=this.deps.registry.register({machineId:t,runtimeAdapter:s,sourceSessionKey:e.agentSessionId,nianSessionId:e.nianSessionId,hostProcessIdentity:this.deps.hostProcessIdentity(),capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:!0,canActivateWhileHostOpen:!0}}),n=await this.deps.bindingClient.create({machineToken:d,bindingIntentId:e.assignmentIntentId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,nianSessionId:e.nianSessionId,runtimeAdapter:s,sessionRegistryKey:o.registryKey,activationCapability:"managed",activationMode:"auto_follow"});return this.deps.registry.setBinding({registryKey:o.registryKey,bindingId:n.binding.id,roomId:n.binding.roomId,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),n}}function r(i){if(i==="codex")return"codex";if(i==="claude")return"claude_code";if(i==="workbuddy"||i==="custom:workbuddy")return"workbuddy";throw new Error(`Agent ${i} does not support managed Room binding.`)}export{a as TrustedManagedRoomBinder};
@@ -0,0 +1,143 @@
1
+ import type { RoomActivationMode, RoomBindingStatus, RoomRuntimeAdapter } from '@shennian/wire';
2
+ export type ManagedRoomSessionAgent = 'codex' | 'claude' | 'workbuddy';
3
+ export type ManagedRoomSessionInput = {
4
+ inviteUrl: string;
5
+ agent: ManagedRoomSessionAgent;
6
+ workDir: string;
7
+ alias: string;
8
+ mode?: RoomActivationMode;
9
+ modelId?: string;
10
+ };
11
+ export type ManagedRoomSessionPhase = 'initialized' | 'session_created' | 'native_session_ready' | 'session_registered' | 'joined';
12
+ export type ManagedRoomSessionFailureStage = 'validate_input' | 'create_session' | 'wait_for_native_session' | 'register_session' | 'join_room';
13
+ type NormalizedInput = Required<Pick<ManagedRoomSessionInput, 'inviteUrl' | 'agent' | 'workDir' | 'alias' | 'mode'>> & {
14
+ modelId: string | null;
15
+ };
16
+ export type ManagedRoomSessionRegistration = {
17
+ registryKey: string;
18
+ };
19
+ export type ManagedRoomJoinResult = {
20
+ outcome: 'joined' | 'pending_approval';
21
+ room: {
22
+ id: string;
23
+ name: string;
24
+ webUrl: string;
25
+ };
26
+ binding: {
27
+ id: string;
28
+ agentIdentityId: string;
29
+ agentMembershipId: string | null;
30
+ status: RoomBindingStatus;
31
+ activationMode: RoomActivationMode;
32
+ };
33
+ idempotent: boolean;
34
+ };
35
+ export type ManagedRoomSessionOperation = {
36
+ schemaVersion: 1;
37
+ operationId: string;
38
+ inputFingerprint: string;
39
+ input: NormalizedInput;
40
+ runtimeAdapter: RoomRuntimeAdapter;
41
+ phase: ManagedRoomSessionPhase;
42
+ nianSessionId: string | null;
43
+ sourceSessionKey: string | null;
44
+ hostProcessIdentity: string | null;
45
+ registryKey: string | null;
46
+ result: ManagedRoomJoinResult | null;
47
+ createdAt: string;
48
+ updatedAt: string;
49
+ };
50
+ export interface ManagedRoomSessionOperationStore {
51
+ load(operationId: string): ManagedRoomSessionOperation | null;
52
+ save(operation: ManagedRoomSessionOperation): void;
53
+ }
54
+ export type ManagedRoomSessionDependencies = {
55
+ operations: ManagedRoomSessionOperationStore;
56
+ /**
57
+ * Must be idempotent for creationKey. A successful call followed by a daemon
58
+ * crash may be repeated before the operation record reaches disk.
59
+ */
60
+ createManagedSession(input: {
61
+ creationKey: string;
62
+ agent: ManagedRoomSessionAgent;
63
+ workDir: string;
64
+ modelId: string | null;
65
+ initialMessage: string;
66
+ }): Promise<{
67
+ nianSessionId: string;
68
+ }>;
69
+ /** Resolves only the native Session created for nianSessionId; never a recent Session. */
70
+ waitForNativeSession(input: {
71
+ nianSessionId: string;
72
+ runtimeAdapter: RoomRuntimeAdapter;
73
+ timeoutMs: number;
74
+ }): Promise<{
75
+ sourceSessionKey: string;
76
+ hostProcessIdentity: string;
77
+ }>;
78
+ /** Persists the trusted observation and exact managed Session Registry record. */
79
+ registerManagedSession(input: {
80
+ nianSessionId: string;
81
+ runtimeAdapter: RoomRuntimeAdapter;
82
+ sourceSessionKey: string;
83
+ hostProcessIdentity: string;
84
+ workDir: string;
85
+ capabilities: {
86
+ canIdentifySession: true;
87
+ canBindAutomatically: true;
88
+ canOpenEmbeddedRoomPage: false;
89
+ canResumeIdleSession: true;
90
+ canActivateWhileHostOpen: true;
91
+ };
92
+ }): Promise<ManagedRoomSessionRegistration>;
93
+ /**
94
+ * Uses daemon-owned machine credentials internally. Public callers never
95
+ * supply Room, Binding, Identity, native Session, or machine credential IDs.
96
+ */
97
+ joinRegisteredSession(input: {
98
+ bindingIntentId: string;
99
+ registryKey: string;
100
+ inviteUrl: string;
101
+ agentAlias: string;
102
+ activationMode: RoomActivationMode;
103
+ activationCapability: 'managed';
104
+ }): Promise<ManagedRoomJoinResult>;
105
+ allowedInviteOrigins?: string[];
106
+ nativeSessionTimeoutMs?: number;
107
+ now?: () => Date;
108
+ };
109
+ export declare class ManagedRoomSessionError extends Error {
110
+ readonly code: string;
111
+ readonly stage: ManagedRoomSessionFailureStage;
112
+ readonly operationId: string | null;
113
+ readonly phase: ManagedRoomSessionPhase;
114
+ readonly nianSessionId: string | null;
115
+ constructor(code: string, message: string, stage: ManagedRoomSessionFailureStage, operationId: string | null, phase: ManagedRoomSessionPhase, nianSessionId: string | null);
116
+ }
117
+ export declare class ManagedRoomSessionOrchestrator {
118
+ private readonly deps;
119
+ private readonly mutexTails;
120
+ constructor(deps: ManagedRoomSessionDependencies);
121
+ execute(operationId: string, rawInput: ManagedRoomSessionInput): Promise<{
122
+ operationId: string;
123
+ phase: 'joined';
124
+ nianSessionId: string;
125
+ result: ManagedRoomJoinResult;
126
+ }>;
127
+ private executeExclusive;
128
+ private save;
129
+ }
130
+ export declare class InMemoryManagedRoomSessionOperationStore implements ManagedRoomSessionOperationStore {
131
+ private readonly records;
132
+ load(operationId: string): ManagedRoomSessionOperation | null;
133
+ save(operation: ManagedRoomSessionOperation): void;
134
+ }
135
+ /** Private per-operation files allow daemon restart recovery without one operation overwriting another. */
136
+ export declare class FileManagedRoomSessionOperationStore implements ManagedRoomSessionOperationStore {
137
+ readonly directory: string;
138
+ constructor(directory: string);
139
+ load(operationId: string): ManagedRoomSessionOperation | null;
140
+ save(operation: ManagedRoomSessionOperation): void;
141
+ private filePath;
142
+ }
143
+ export {};
@@ -0,0 +1,3 @@
1
+ import S from"node:crypto";import a from"node:fs";import y from"node:path";class h extends Error{code;stage;operationId;phase;nianSessionId;constructor(i,o,t,r,n,s){super(o),this.code=i,this.stage=t,this.operationId=r,this.phase=n,this.nianSessionId=s,this.name="ManagedRoomSessionError"}}const j=/^[A-Za-z0-9_-]{8,96}$/,R=/^[A-Za-z0-9_.:@/-]{1,512}$/,w=/^[a-f0-9]{64}$/,k=/^[A-Za-z0-9_-]{43}$/,D=new Set(["inviteUrl","agent","workDir","alias","mode","modelId"]),K=new Set(["codex","claude","workbuddy"]),N=new Set(["auto_follow","mention_only","paused"]),P=["https://shennian.net"],$=3e4;class B{deps;mutexTails=new Map;constructor(i){this.deps=i}async execute(i,o){_(i);let t;const r=this.mutexTails.get(i)??Promise.resolve(),n=new Promise(l=>{t=l}),s=r.then(()=>n);this.mutexTails.set(i,s),await r;try{return await this.executeExclusive(i,o)}finally{t(),this.mutexTails.get(i)===s&&this.mutexTails.delete(i)}}async executeExclusive(i,o){let t;try{t=I(o,this.deps.allowedInviteOrigins??P)}catch(s){throw m(s,"validate_input",i,null)}const r=b(t);let n=this.deps.operations.load(i);if(n&&n.inputFingerprint!==r)throw new h("managed_room_operation_conflict","This operation ID was already used with different inputs.","validate_input",i,n.phase,n.nianSessionId);if(!n){const s=(this.deps.now?.()??new Date).toISOString();n={schemaVersion:1,operationId:i,inputFingerprint:r,input:t,runtimeAdapter:v(t.agent),phase:"initialized",nianSessionId:null,sourceSessionKey:null,hostProcessIdentity:null,registryKey:null,result:null,createdAt:s,updatedAt:s},this.save(n)}if(n.phase==="joined")return E(n);if(n.phase==="initialized")try{const s=await this.deps.createManagedSession({creationKey:`room-managed-session:${i}`,agent:t.agent,workDir:t.workDir,modelId:t.modelId,initialMessage:U()});c(s.nianSessionId,"nian_session_id"),n.nianSessionId=s.nianSessionId,n.phase="session_created",this.save(n)}catch(s){throw m(s,"create_session",i,n)}if(n.phase==="session_created")try{const s=await this.deps.waitForNativeSession({nianSessionId:n.nianSessionId,runtimeAdapter:n.runtimeAdapter,timeoutMs:this.deps.nativeSessionTimeoutMs??$});A(s.sourceSessionKey),c(s.hostProcessIdentity,"host_process_identity"),n.sourceSessionKey=s.sourceSessionKey,n.hostProcessIdentity=s.hostProcessIdentity,n.phase="native_session_ready",this.save(n)}catch(s){throw m(s,"wait_for_native_session",i,n)}if(n.phase==="native_session_ready")try{const s=await this.deps.registerManagedSession({nianSessionId:n.nianSessionId,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,hostProcessIdentity:n.hostProcessIdentity,workDir:t.workDir,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:!0,canActivateWhileHostOpen:!0}});if(!w.test(s.registryKey))throw u("registry_key");n.registryKey=s.registryKey,n.phase="session_registered",this.save(n)}catch(s){throw m(s,"register_session",i,n)}if(n.phase==="session_registered")try{n.result=await this.deps.joinRegisteredSession({bindingIntentId:`room-managed-session-join:${i}`,registryKey:n.registryKey,inviteUrl:t.inviteUrl,agentAlias:t.alias,activationMode:t.mode,activationCapability:"managed"}),O(n.result),n.phase="joined",this.save(n)}catch(s){throw m(s,"join_room",i,n)}return E(n)}save(i){i.updatedAt=(this.deps.now?.()??new Date).toISOString(),this.deps.operations.save(g(i))}}class G{records=new Map;load(i){const o=this.records.get(i);return o?g(o):null}save(i){this.records.set(i.operationId,g(i))}}class Z{directory;constructor(i){this.directory=i}load(i){_(i);const o=this.filePath(i);try{const t=a.lstatSync(o);if(!t.isFile()||t.isSymbolicLink())throw p();process.platform!=="win32"&&(t.mode&63)!==0&&a.chmodSync(o,384);const r=JSON.parse(a.readFileSync(o,"utf8"));return M(r,i),g(r)}catch(t){if(t.code==="ENOENT")return null;throw t}}save(i){_(i.operationId),M(i,i.operationId),a.mkdirSync(this.directory,{recursive:!0,mode:448});const o=a.lstatSync(this.directory);if(!o.isDirectory()||o.isSymbolicLink())throw p();process.platform!=="win32"&&a.chmodSync(this.directory,448);const t=this.filePath(i.operationId),r=`${t}.tmp-${process.pid}-${S.randomBytes(6).toString("hex")}`;try{if(a.writeFileSync(r,`${JSON.stringify(i,null,2)}
2
+ `,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&a.chmodSync(r,384),process.platform==="win32"&&a.existsSync(t)){const n=a.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw p();a.unlinkSync(t)}a.renameSync(r,t)}finally{try{a.unlinkSync(r)}catch{}}}filePath(i){return y.join(this.directory,`${i}.json`)}}function I(e,i){(!e||typeof e!="object"||Array.isArray(e))&&d("input");const o=e;Object.keys(o).find(T=>!D.has(T))&&d("unsupported_field");const r=o.agent;(typeof r!="string"||!K.has(r))&&d("agent");const n=f(o.workDir,"work_dir",4096);(!y.isAbsolute(n)||y.normalize(n)!==n)&&d("work_dir");const s=f(o.alias,"alias",80),l=o.mode??"auto_follow";(typeof l!="string"||!N.has(l))&&d("mode");const x=o.modelId==null?null:f(o.modelId,"model_id",191);return{inviteUrl:F(o.inviteUrl,i),agent:r,workDir:n,alias:s,mode:l,modelId:x}}function F(e,i){const o=f(e,"invite_url",2048);let t;try{t=new URL(o)}catch{d("invite_url")}(!new Set(i.map(l=>new URL(l).origin)).has(t.origin)||t.username||t.password||t.search||t.hash)&&d("invite_url");const n=t.pathname.split("/").filter(Boolean),s=n.at(-1);return(!s||!k.test(s)||n.at(-2)!=="join")&&d("invite_url"),t.toString()}function f(e,i,o){typeof e!="string"&&d(i);const t=e.trim();return(!t||t.length>o||/[\0\r\n]/.test(t))&&d(i),t}function _(e){if(!j.test(e))throw new h("invalid_managed_room_operation_id","Managed Room operation ID is invalid.","validate_input",null,"initialized",null)}function v(e){return e==="claude"?"claude_code":e}function b(e){return S.createHash("sha256").update(JSON.stringify(e),"utf8").digest("hex")}function c(e,i){if(!R.test(e))throw u(i)}function A(e){if(!e||e.length>1024||/[\0\r\n]/.test(e))throw u("source_session_key")}function O(e){if(c(e.room?.id,"room_id"),c(e.binding?.id,"binding_id"),c(e.binding?.agentIdentityId,"agent_identity_id"),e.binding.agentMembershipId!==null&&c(e.binding.agentMembershipId,"agent_membership_id"),e.outcome!=="joined"&&e.outcome!=="pending_approval")throw u("join_outcome");if(e.outcome==="joined"&&e.binding.status!=="active"&&e.binding.status!=="paused"||e.outcome==="pending_approval"&&e.binding.status!=="pending_approval"||e.binding.status==="pending_approval"&&e.binding.agentMembershipId!==null||(e.binding.status==="active"||e.binding.status==="paused")&&e.binding.agentMembershipId===null)throw u("join_binding_state")}function U(){return["\u8FD9\u662F\u4E00\u4E2A\u7531\u795E\u5FF5\u521B\u5EFA\u7684\u53D7\u7BA1 Agent \u5BF9\u8BDD\u3002","\u8BF7\u5148\u4FDD\u6301\u7A7A\u95F2\uFF1B\u795E\u5FF5\u6B63\u5728\u4E3A\u8BE5\u7CBE\u786E\u5BF9\u8BDD\u5EFA\u7ACB\u7FA4\u804A\u8FDE\u63A5\uFF0C\u5B8C\u6210\u540E\u4F1A\u53D1\u9001\u540E\u7EED\u6D88\u606F\u3002"].join(`
3
+ `)}function E(e){if(!e.nianSessionId||!e.result||e.phase!=="joined")throw u("operation_state");return{operationId:e.operationId,phase:"joined",nianSessionId:e.nianSessionId,result:g(e.result)}}function m(e,i,o,t){if(e instanceof h)return e;const r=e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"?e.code:`managed_room_${i}_failed`;return new h(r,`Managed Room Session failed during ${i}. The created conversation was preserved for retry.`,i,o,t?.phase??"initialized",t?.nianSessionId??null)}function d(e){throw new h(`invalid_managed_room_${e}`,`Managed Room Session ${e} is invalid.`,"validate_input",null,"initialized",null)}function u(e){return Object.assign(new Error(`Managed Room dependency returned an invalid ${e}.`),{code:`invalid_managed_room_${e}`})}function M(e,i){if(!e||e.schemaVersion!==1||e.operationId!==i||!/^[a-f0-9]{64}$/.test(e.inputFingerprint)||!["initialized","session_created","native_session_ready","session_registered","joined"].includes(e.phase)||!e.input||v(e.input.agent)!==e.runtimeAdapter)throw p();const o=I(e.input,[new URL(e.input.inviteUrl).origin]);if(b(o)!==e.inputFingerprint||(e.nianSessionId!==null&&c(e.nianSessionId,"nian_session_id"),e.sourceSessionKey!==null&&A(e.sourceSessionKey),e.hostProcessIdentity!==null&&c(e.hostProcessIdentity,"host_process_identity"),e.registryKey!==null&&!w.test(e.registryKey)))throw p();e.result!==null&&O(e.result)}function p(){return Object.assign(new Error("Managed Room Session operation store is unsafe or corrupt."),{code:"unsafe_managed_room_operation_store"})}function g(e){return JSON.parse(JSON.stringify(e))}export{Z as FileManagedRoomSessionOperationStore,G as InMemoryManagedRoomSessionOperationStore,h as ManagedRoomSessionError,B as ManagedRoomSessionOrchestrator};
@@ -0,0 +1,33 @@
1
+ import { HttpRoomActionClient } from './room-action-client.js';
2
+ import { RoomJsonlMirror } from './jsonl-mirror.js';
3
+ import { RoomSessionRegistry, type RoomSessionRegistration } from './session-registry.js';
4
+ import type { RoomMessageView } from '@shennian/wire';
5
+ type Mirror = Pick<RoomJsonlMirror, 'append' | 'markAccessRevoked' | 'readState' | 'recordSyncError'>;
6
+ type Timer = ReturnType<typeof setTimeout>;
7
+ export declare class RoomMirrorSyncService {
8
+ private readonly deps;
9
+ private timer;
10
+ private running;
11
+ private started;
12
+ constructor(deps: {
13
+ registry: RoomSessionRegistry;
14
+ actionClient: Pick<HttpRoomActionClient, 'status' | 'read'>;
15
+ getMachineCredentials: () => {
16
+ machineToken?: string;
17
+ };
18
+ mirrorFactory?: (roomId: string) => Mirror;
19
+ initialDelayMs?: number;
20
+ intervalMs?: number;
21
+ maxPagesPerRun?: number;
22
+ setTimeout?: (handler: () => void, delayMs: number) => Timer;
23
+ clearTimeout?: (timer: Timer) => void;
24
+ onMessages?: (roomId: string, messages: RoomMessageView[], registrations: RoomSessionRegistration[]) => Promise<void>;
25
+ });
26
+ start(): void;
27
+ stop(): void;
28
+ runOnce(): Promise<void>;
29
+ private syncRoom;
30
+ private pullPages;
31
+ private arm;
32
+ }
33
+ export {};
@@ -0,0 +1 @@
1
+ import{RoomActionHttpError as m}from"./room-action-client.js";import{RoomJsonlMirror as h}from"./jsonl-mirror.js";class S{deps;timer=null;running=!1;started=!1;constructor(e){this.deps=e}start(){this.started||(this.started=!0,this.arm(this.deps.initialDelayMs??0))}stop(){this.started=!1,this.timer&&(this.deps.clearTimeout??clearTimeout)(this.timer),this.timer=null}async runOnce(){if(this.running)return;const e=this.deps.getMachineCredentials().machineToken?.trim();if(e){this.running=!0;try{const i=l(this.deps.registry.list());for(const[r,o]of i)await this.syncRoom(r,o,e)}finally{this.running=!1}}}async syncRoom(e,i,r){const o=(this.deps.mirrorFactory??(t=>new h(t)))(e),u=i.filter(t=>t.binding&&(t.binding.status==="active"||t.binding.status==="paused"));if(u.length===0){i.some(t=>t.binding?.status==="detached")&&!i.some(t=>t.binding?.status==="pending_approval")&&o.markAccessRevoked();return}let a=0,c=null;for(const t of u){const d=t.binding,g={machineToken:r,roomId:e,bindingId:d.bindingId};try{const s=await this.deps.actionClient.status(g);if(this.deps.registry.setBinding({registryKey:t.registryKey,bindingId:s.bindingId,roomId:s.roomId,agentIdentityId:s.agentIdentityId,agentMembershipId:s.agentMembershipId,status:s.status,activationMode:s.activationMode}),s.status==="detached"){a+=1;continue}if(s.status==="pending_approval")continue;c??=g}catch(s){if(p(s)){this.deps.registry.detachBinding(t.registryKey),a+=1;continue}o.recordSyncError(f(s));return}}if(c){const t=i.map(d=>this.deps.registry.getByRegistryKey(d.registryKey)??d);await this.pullPages(o,c,e,t);return}a===u.length&&o.markAccessRevoked()}async pullPages(e,i,r,o){const u=this.deps.maxPagesPerRun??100;let a=e.readState().lastSyncedSequence;for(let c=0;c<u;c+=1){const t=await this.deps.actionClient.read({...i,afterSequence:a,limit:100}),d=e.append({messages:t.messages,historyFromSequence:t.historyFromSequence,roomName:t.roomName});if(this.deps.onMessages&&await this.deps.onMessages(r,t.messages,o),!t.hasMore)return;if(d.lastSyncedSequence===a)throw new m("room_sync_no_progress","Room mirror synchronization made no progress.");a=d.lastSyncedSequence}}arm(e){if(!this.started||this.timer)return;const i=this.deps.setTimeout??setTimeout;this.timer=i(()=>{this.timer=null,this.runOnce().catch(()=>{}).finally(()=>{this.started&&this.arm(this.deps.intervalMs??5e3)})},e),this.timer.unref?.()}}function l(n){const e=new Map;for(const i of n){if(!i.binding||i.lifecycleState==="closed")continue;const r=e.get(i.binding.roomId)??[];r.push(i),e.set(i.binding.roomId,r)}return e}function p(n){return n instanceof m&&(n.code==="room_binding_unavailable"||n.code==="room_action_http_403")}function f(n){return n instanceof m?n.code:"room_sync_failed"}export{S as RoomMirrorSyncService};
@@ -0,0 +1,11 @@
1
+ import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
2
+ import type { RoomSessionRegistry } from './session-registry.js';
3
+ import type { HttpRoomActionClient } from './room-action-client.js';
4
+ export declare function createRoomOperatorIpcHandlers(input: {
5
+ registry: Pick<RoomSessionRegistry, 'list'>;
6
+ actionClient: Pick<HttpRoomActionClient, 'createOperatorRoom'>;
7
+ getMachineCredentials: () => {
8
+ machineId?: string;
9
+ machineToken?: string;
10
+ };
11
+ }): Record<string, DaemonIpcHandler>;
@@ -0,0 +1 @@
1
+ import{DaemonIpcError as r}from"../daemon-ipc/protocol.js";function w(e){return{"room-operator.inspect":n=>{const o=u(n),t=e.getMachineCredentials(),i=t.machineId?.trim(),l=!!(i&&t.machineToken?.trim()),c=i?e.registry.list().filter(s=>s.machineId===i):[],m=c.filter(s=>s.binding!==null),d=h(m);return a(o==="status"?{source:"local_registry",signedIn:l,registeredSessions:c.length,boundSessions:m.length,rooms:d.length}:{source:"local_registry",complete:!1,rooms:d})},"room-operator.create":async n=>{const o=f(n),t=e.getMachineCredentials();if(!t.machineId?.trim()||!t.machineToken?.trim())throw new r("cli_not_signed_in","Sign in to the Shennian CLI first.");try{return a(await e.actionClient.createOperatorRoom({machineToken:t.machineToken,name:o}))}catch(i){throw g(i)}}}}function a(e){return JSON.parse(JSON.stringify(e))}function u(e){if(!e||Array.isArray(e)||typeof e!="object")throw new r("invalid_params","Room operator parameters are required.");const n=e;if(Object.keys(n).length!==1||n.action!=="status"&&n.action!=="list")throw new r("invalid_params","Room operator action is invalid.");return n.action}function f(e){if(!e||Array.isArray(e)||typeof e!="object")throw new r("invalid_params","Room operator parameters are required.");const n=e;if(Object.keys(n).length!==1||typeof n.name!="string")throw new r("invalid_params","Room operator create parameters are invalid.");const o=n.name.trim();if(!o||o.length>80)throw new r("invalid_params","Room name must contain 1 to 80 characters.");return o}function g(e){return e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"&&"message"in e&&typeof e.message=="string"?new r(e.code,e.message):new r("room_operator_failed","Shennian Room operator action failed.")}function h(e){const n=new Map;for(const o of e){const t=o.binding;if(!t)continue;const i=n.get(t.roomId)??{sessions:0,runtimes:new Set,statuses:new Set,modes:new Set};i.sessions+=1,i.runtimes.add(o.runtimeAdapter),i.statuses.add(t.status),i.modes.add(t.activationMode),n.set(t.roomId,i)}return[...n.entries()].sort(([o],[t])=>o.localeCompare(t)).map(([o,t])=>({roomId:o,sessions:t.sessions,runtimes:[...t.runtimes].sort(),bindingStatuses:[...t.statuses].sort(),activationModes:[...t.modes].sort()}))}export{w as createRoomOperatorIpcHandlers};
@@ -0,0 +1,69 @@
1
+ export type AuthorizedActionStatus = 'waiting_user' | 'approved' | 'executing' | 'completed' | 'failed' | 'expired';
2
+ export type JsonValue = null | boolean | number | string | JsonValue[] | {
3
+ [key: string]: JsonValue;
4
+ };
5
+ export type PendingAuthorizedAction = {
6
+ schemaVersion: 1;
7
+ id: string;
8
+ loginIntentId: string;
9
+ publicLoginUrl: string;
10
+ actionType: string;
11
+ canonicalActionPayload: JsonValue;
12
+ actionPayloadHash: string;
13
+ idempotencyKey: string;
14
+ deviceSecretHash: string;
15
+ status: AuthorizedActionStatus;
16
+ expiresAt: string;
17
+ createdAt: string;
18
+ updatedAt: string;
19
+ browserOpenedAt?: string;
20
+ approvedAt?: string;
21
+ executingAt?: string;
22
+ completedAt?: string;
23
+ failedAt?: string;
24
+ errorCode?: string;
25
+ result?: JsonValue;
26
+ };
27
+ type CreatePendingActionInput = {
28
+ id?: string;
29
+ loginIntentId: string;
30
+ publicLoginUrl: string;
31
+ actionType: string;
32
+ actionPayload: JsonValue;
33
+ idempotencyKey: string;
34
+ deviceSecret: string;
35
+ expiresAt: string;
36
+ now?: Date;
37
+ };
38
+ export declare class PendingAuthorizedActionError extends Error {
39
+ readonly code: string;
40
+ constructor(code: string, message: string);
41
+ }
42
+ export declare function canonicalizeActionPayload(value: JsonValue): string;
43
+ export declare function hashActionPayload(value: JsonValue): string;
44
+ export declare function hashPendingDeviceSecret(secret: string): string;
45
+ export declare class PendingAuthorizedActionStore {
46
+ readonly directory: string;
47
+ constructor(rootDir?: string);
48
+ create(input: CreatePendingActionInput): PendingAuthorizedAction;
49
+ get(id: string): PendingAuthorizedAction | null;
50
+ list(): PendingAuthorizedAction[];
51
+ findByIdempotencyKey(idempotencyKey: string): PendingAuthorizedAction | null;
52
+ listRecoverable(now?: Date): PendingAuthorizedAction[];
53
+ readDeviceSecret(id: string): string;
54
+ markApproved(id: string, now?: Date): PendingAuthorizedAction;
55
+ markBrowserOpened(id: string, now?: Date): PendingAuthorizedAction;
56
+ markExecuting(id: string, now?: Date): PendingAuthorizedAction;
57
+ markCompleted(id: string, result: JsonValue, now?: Date): PendingAuthorizedAction;
58
+ markFailed(id: string, errorCode: string, now?: Date): PendingAuthorizedAction;
59
+ private transition;
60
+ private update;
61
+ private ensureDirectory;
62
+ private recordPath;
63
+ private secretPath;
64
+ private writeRecord;
65
+ private writeSecret;
66
+ private atomicWrite;
67
+ private removeSecret;
68
+ }
69
+ export {};
@@ -0,0 +1,3 @@
1
+ import d from"node:crypto";import c from"node:fs";import m from"node:path";import{resolveShennianPath as b}from"../config/index.js";const u=/^[A-Za-z0-9_-]{8,128}$/,w=/^[a-z][a-z0-9_.-]{0,63}$/,S=/^[a-f0-9]{64}$/,f=new Set(["completed","failed","expired"]);class r extends Error{code;constructor(e,t){super(t),this.code=e,this.name="PendingAuthorizedActionError"}}function I(i){return l(i)}function _(i){return d.createHash("sha256").update(I(i),"utf8").digest("hex")}function v(i){return d.createHash("sha256").update(i,"utf8").digest("hex")}class k{directory;constructor(e=b("runtime","pending-authorized-actions")){this.directory=e}create(e){if(y(e.loginIntentId,"loginIntentId"),O(e.publicLoginUrl,e.loginIntentId),!w.test(e.actionType))throw new r("invalid_action_type","Invalid authorized action type.");if(!e.idempotencyKey.trim()||e.idempotencyKey.length>191)throw new r("invalid_idempotency_key","Invalid idempotency key.");if(e.deviceSecret.length<32||e.deviceSecret.length>256)throw new r("invalid_device_secret","Invalid device secret.");const t=new Date(e.expiresAt);if(!Number.isFinite(t.getTime()))throw new r("invalid_expiry","Invalid authorization expiry.");this.ensureDirectory();const n=x(e.actionPayload),o=_(n),a=this.list().find(p=>p.idempotencyKey===e.idempotencyKey);if(a){if(a.actionType!==e.actionType||a.actionPayloadHash!==o)throw new r("idempotency_payload_conflict","The idempotency key is already bound to a different action.");return a}const s=(e.now??new Date).toISOString(),h=e.id??d.randomUUID();y(h,"id");const g={schemaVersion:1,id:h,loginIntentId:e.loginIntentId,publicLoginUrl:e.publicLoginUrl,actionType:e.actionType,canonicalActionPayload:n,actionPayloadHash:o,idempotencyKey:e.idempotencyKey,deviceSecretHash:v(e.deviceSecret),status:"waiting_user",expiresAt:t.toISOString(),createdAt:s,updatedAt:s};this.writeSecret(h,e.deviceSecret);try{this.writeRecord(g)}catch(p){throw this.removeSecret(h),p}return g}get(e){y(e,"id");try{const t=this.recordPath(e),n=c.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw new r("unsafe_pending_action","Pending action is not a regular file.");return A(JSON.parse(c.readFileSync(t,"utf8")))}catch(t){if(N(t))return null;throw t instanceof r?t:new r("corrupt_pending_action","Pending authorized action is corrupt.")}}list(){return this.ensureDirectory(),c.readdirSync(this.directory).filter(e=>e.endsWith(".json")&&!e.includes(".tmp-")).map(e=>this.get(e.slice(0,-5))).filter(e=>e!==null).sort((e,t)=>e.createdAt.localeCompare(t.createdAt))}findByIdempotencyKey(e){return this.list().find(t=>t.idempotencyKey===e)??null}listRecoverable(e=new Date){const t=[];for(const n of this.list())if(!f.has(n.status)){if(new Date(n.expiresAt)<=e){this.update(n.id,{status:"expired",errorCode:"authorization_expired"},e),this.removeSecret(n.id);continue}t.push(n)}return t}readDeviceSecret(e){const t=this.get(e);if(!t||f.has(t.status))throw new r("pending_action_unavailable","Pending action is unavailable.");let n;try{const o=this.secretPath(e),a=c.lstatSync(o);if(!a.isFile()||a.isSymbolicLink())throw new r("unsafe_device_secret","Device authorization secret is not a regular file.");n=c.readFileSync(o,"utf8").trim()}catch(o){throw o instanceof r?o:new r("device_secret_missing","Device authorization secret is missing.")}if(!$(n,t.deviceSecretHash))throw new r("device_secret_mismatch","Device authorization secret is invalid.");return n}markApproved(e,t=new Date){return this.transition(e,["waiting_user","approved"],"approved",{approvedAt:t.toISOString()},t)}markBrowserOpened(e,t=new Date){const n=this.get(e);if(!n)throw new r("pending_action_not_found","Pending action not found.");return n.browserOpenedAt||n.status!=="waiting_user"?n:this.update(e,{browserOpenedAt:t.toISOString()},t)}markExecuting(e,t=new Date){const n=this.transition(e,["approved"],"executing",{executingAt:t.toISOString()},t);return this.removeSecret(e),n}markCompleted(e,t,n=new Date){const o=this.transition(e,["executing","completed"],"completed",{completedAt:n.toISOString(),result:t,errorCode:void 0},n);return this.removeSecret(e),o}markFailed(e,t,n=new Date){const o=this.transition(e,["waiting_user","approved","executing","failed"],"failed",{failedAt:n.toISOString(),errorCode:t.slice(0,120)},n);return this.removeSecret(e),o}transition(e,t,n,o,a){const s=this.get(e);if(!s)throw new r("pending_action_not_found","Pending action not found.");if(!t.includes(s.status))throw new r("invalid_pending_action_transition",`Cannot transition ${s.status} to ${n}.`);return s.status===n&&f.has(n)?s:this.update(e,{...o,status:n},a)}update(e,t,n){const o=this.get(e);if(!o)throw new r("pending_action_not_found","Pending action not found.");const a=A({...o,...t,id:o.id,updatedAt:n.toISOString()});return this.writeRecord(a),a}ensureDirectory(){c.mkdirSync(this.directory,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(this.directory,448)}recordPath(e){return m.join(this.directory,`${e}.json`)}secretPath(e){return m.join(this.directory,`${e}.secret`)}writeRecord(e){this.atomicWrite(this.recordPath(e.id),`${JSON.stringify(e,null,2)}
2
+ `)}writeSecret(e,t){this.atomicWrite(this.secretPath(e),`${t}
3
+ `)}atomicWrite(e,t){this.ensureDirectory();const n=`${e}.tmp-${process.pid}-${d.randomBytes(6).toString("hex")}`;try{c.writeFileSync(n,t,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&c.chmodSync(n,384),c.renameSync(n,e),process.platform!=="win32"&&c.chmodSync(e,384)}finally{try{c.unlinkSync(n)}catch{}}}removeSecret(e){try{c.unlinkSync(this.secretPath(e))}catch{}}}function l(i){if(i===null||typeof i=="boolean"||typeof i=="string")return JSON.stringify(i);if(typeof i=="number"){if(!Number.isFinite(i))throw new r("invalid_action_payload","Numbers must be finite.");return JSON.stringify(i)}if(Array.isArray(i))return`[${i.map(l).join(",")}]`;if(typeof i!="object")throw new r("invalid_action_payload","Action payload must be JSON.");const e=Object.getPrototypeOf(i);if(e!==Object.prototype&&e!==null)throw new r("invalid_action_payload","Action payload must be plain JSON.");return`{${Object.keys(i).sort().map(t=>`${JSON.stringify(t)}:${l(i[t])}`).join(",")}}`}function x(i){return JSON.parse(l(i))}function y(i,e){if(!u.test(i))throw new r(`invalid_${e}`,`Invalid ${e}.`)}function A(i){if(!i||typeof i!="object")throw new r("corrupt_pending_action","Pending action must be an object.");const e=i;if(e.schemaVersion!==1||typeof e.id!="string"||!u.test(e.id)||typeof e.loginIntentId!="string"||!u.test(e.loginIntentId)||typeof e.publicLoginUrl!="string"||!P(e.publicLoginUrl,e.loginIntentId)||typeof e.actionType!="string"||!w.test(e.actionType)||typeof e.idempotencyKey!="string"||!e.idempotencyKey||typeof e.actionPayloadHash!="string"||!S.test(e.actionPayloadHash)||typeof e.deviceSecretHash!="string"||!S.test(e.deviceSecretHash)||!["waiting_user","approved","executing","completed","failed","expired"].includes(e.status??"")||typeof e.expiresAt!="string"||!Number.isFinite(new Date(e.expiresAt).getTime())||typeof e.createdAt!="string"||typeof e.updatedAt!="string")throw new r("corrupt_pending_action","Pending action has invalid fields.");if(e.browserOpenedAt!==void 0&&(typeof e.browserOpenedAt!="string"||!Number.isFinite(new Date(e.browserOpenedAt).getTime())))throw new r("corrupt_pending_action","Pending action has invalid browser state.");if(_(e.canonicalActionPayload)!==e.actionPayloadHash)throw new r("action_payload_tampered","Pending action payload hash mismatch.");return e}function O(i,e){if(!P(i,e))throw new r("invalid_public_login_url","Invalid Shennian authorization URL.")}function P(i,e){try{const t=new URL(i);return t.pathname!==`/authorize/${e}`||t.search||t.hash?!1:t.protocol==="https:"&&(t.hostname==="shennian.net"||t.hostname.endsWith(".shennian.net"))?!0:t.protocol==="http:"&&D(t.hostname)}catch{return!1}}function D(i){return i==="127.0.0.1"||i==="localhost"||i==="::1"||/^10\./.test(i)||/^192\.168\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i)}function $(i,e){const t=Buffer.from(v(i),"hex"),n=Buffer.from(e,"hex");return t.length===n.length&&d.timingSafeEqual(t,n)}function N(i){return typeof i=="object"&&i!==null&&"code"in i&&i.code==="ENOENT"}export{r as PendingAuthorizedActionError,k as PendingAuthorizedActionStore,I as canonicalizeActionPayload,_ as hashActionPayload,v as hashPendingDeviceSecret};