@crouton-kit/crouter 0.3.28 → 0.3.30

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 (324) hide show
  1. package/dist/build-root.d.ts +15 -7
  2. package/dist/build-root.js +48 -34
  3. package/dist/builtin-memory/crouter-development/marketplaces.md +7 -6
  4. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +1 -1
  5. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +1 -1
  6. package/dist/builtin-memory/crouter-development/personas.md +1 -1
  7. package/dist/builtin-memory/crouter-development/plugins.md +20 -18
  8. package/dist/builtin-memory/design.md +1 -1
  9. package/dist/builtin-memory/development.md +1 -1
  10. package/dist/builtin-memory/internal/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
  12. package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
  13. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  14. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  15. package/dist/builtin-memory/planning.md +1 -1
  16. package/dist/builtin-memory/spec.md +6 -4
  17. package/dist/builtin-personas/orchestration-kernel.md +4 -5
  18. package/dist/builtin-personas/runtime-base.md +5 -4
  19. package/dist/builtin-personas/spec/PERSONA.md +8 -4
  20. package/dist/builtin-personas/spec/orchestrator.md +5 -3
  21. package/dist/builtin-personas/spec/requirements/PERSONA.md +10 -0
  22. package/dist/builtin-views/canvas/core.mjs +586 -0
  23. package/dist/builtin-views/canvas/text.mjs +58 -0
  24. package/dist/builtin-views/canvas/tui.mjs +165 -0
  25. package/dist/builtin-views/canvas/web.jsx +120 -0
  26. package/dist/builtin-views/git-pr/core.mjs +673 -0
  27. package/dist/builtin-views/git-pr/text.mjs +84 -0
  28. package/dist/builtin-views/git-pr/tui.mjs +302 -0
  29. package/dist/builtin-views/git-pr/web.jsx +216 -0
  30. package/dist/builtin-views/inbox/_lib/render.mjs +1 -3
  31. package/dist/builtin-views/inbox/core.mjs +1273 -0
  32. package/dist/builtin-views/inbox/text.mjs +73 -0
  33. package/dist/builtin-views/inbox/tui.mjs +312 -0
  34. package/dist/builtin-views/inbox/web.jsx +188 -0
  35. package/dist/builtin-views/linkedin/core.mjs +906 -0
  36. package/dist/builtin-views/linkedin/text.mjs +69 -0
  37. package/dist/builtin-views/linkedin/tui.mjs +427 -0
  38. package/dist/builtin-views/linkedin/web.jsx +206 -0
  39. package/dist/builtin-views/workspace-sidebar/core.mjs +621 -0
  40. package/dist/builtin-views/workspace-sidebar/text.mjs +53 -0
  41. package/dist/builtin-views/workspace-sidebar/tui.mjs +142 -0
  42. package/dist/builtin-views/workspace-sidebar/web.jsx +109 -0
  43. package/dist/cli.js +22 -17
  44. package/dist/clients/attach/__tests__/action-parity.test.js +48 -0
  45. package/dist/clients/attach/__tests__/clipboard-image.test.js +76 -0
  46. package/dist/clients/attach/__tests__/editor-newline.test.js +57 -0
  47. package/dist/clients/attach/__tests__/git-info.test.js +31 -0
  48. package/dist/clients/attach/__tests__/onrequest-wired.test.js +20 -0
  49. package/dist/clients/attach/__tests__/slash-quit-copy.test.js +42 -0
  50. package/dist/clients/attach/__tests__/titled-editor.test.js +39 -0
  51. package/dist/clients/attach/attach-cmd.js +282 -46
  52. package/dist/clients/attach/auth-pickers.d.ts +9 -0
  53. package/dist/clients/attach/auth-pickers.js +194 -0
  54. package/dist/clients/attach/canvas-panels.d.ts +4 -2
  55. package/dist/clients/attach/canvas-panels.js +45 -17
  56. package/dist/clients/attach/chat-view.d.ts +27 -2
  57. package/dist/clients/attach/chat-view.js +98 -9
  58. package/dist/clients/attach/clipboard-image.d.ts +47 -8
  59. package/dist/clients/attach/clipboard-image.js +150 -26
  60. package/dist/clients/attach/clipboard-text.d.ts +3 -0
  61. package/dist/clients/attach/clipboard-text.js +39 -0
  62. package/dist/clients/attach/config-load.d.ts +54 -7
  63. package/dist/clients/attach/config-load.js +99 -3
  64. package/dist/clients/attach/context-message.d.ts +12 -0
  65. package/dist/clients/attach/context-message.js +72 -0
  66. package/dist/clients/attach/git-info.d.ts +20 -0
  67. package/dist/clients/attach/git-info.js +44 -0
  68. package/dist/clients/attach/graph-overlay.d.ts +13 -4
  69. package/dist/clients/attach/graph-overlay.js +59 -19
  70. package/dist/clients/attach/input-controller.d.ts +75 -13
  71. package/dist/clients/attach/input-controller.js +305 -67
  72. package/dist/clients/attach/pickers.d.ts +58 -0
  73. package/dist/clients/attach/pickers.js +171 -0
  74. package/dist/clients/attach/slash-commands.d.ts +22 -0
  75. package/dist/clients/attach/slash-commands.js +91 -25
  76. package/dist/clients/attach/titled-editor.d.ts +33 -0
  77. package/dist/clients/attach/titled-editor.js +84 -0
  78. package/dist/clients/attach/view-socket.d.ts +18 -1
  79. package/dist/clients/attach/view-socket.js +70 -1
  80. package/dist/clients/web/dev-server.d.ts +7 -0
  81. package/dist/clients/web/dev-server.js +59 -0
  82. package/dist/clients/web/events.d.ts +14 -0
  83. package/dist/clients/web/events.js +151 -0
  84. package/dist/clients/web/server.d.ts +18 -0
  85. package/dist/clients/web/server.js +450 -0
  86. package/dist/clients/web/web-cmd.d.ts +2 -0
  87. package/dist/clients/web/web-cmd.js +120 -0
  88. package/dist/commands/canvas.js +1 -2
  89. package/dist/commands/chord.js +4 -25
  90. package/dist/commands/dashboard.js +5 -1
  91. package/dist/commands/memory/__tests__/lint-schema.test.js +6 -6
  92. package/dist/commands/memory/lint.js +14 -2
  93. package/dist/commands/memory/read.js +2 -2
  94. package/dist/commands/memory/shared.d.ts +7 -1
  95. package/dist/commands/memory/shared.js +18 -3
  96. package/dist/commands/memory/write.js +8 -4
  97. package/dist/commands/memory.js +4 -4
  98. package/dist/commands/node.d.ts +1 -1
  99. package/dist/commands/node.js +64 -65
  100. package/dist/commands/pkg.js +1 -2
  101. package/dist/commands/revive.js +11 -3
  102. package/dist/commands/sys/sync.d.ts +1 -0
  103. package/dist/commands/sys/sync.js +187 -0
  104. package/dist/commands/sys.js +3 -2
  105. package/dist/commands/view-cycle.js +2 -2
  106. package/dist/commands/view-list.js +8 -8
  107. package/dist/commands/view-new.js +21 -17
  108. package/dist/commands/view-pick.js +1 -1
  109. package/dist/commands/view-run.js +35 -14
  110. package/dist/commands/view.js +8 -6
  111. package/dist/commands/workspace.d.ts +2 -0
  112. package/dist/commands/workspace.js +161 -0
  113. package/dist/core/__tests__/broker-double-spawn.test.d.ts +1 -0
  114. package/dist/core/__tests__/broker-double-spawn.test.js +143 -0
  115. package/dist/core/__tests__/broker-fork-seam.test.d.ts +1 -0
  116. package/dist/core/__tests__/broker-fork-seam.test.js +102 -0
  117. package/dist/core/__tests__/broker-sdk-wiring.test.js +2 -0
  118. package/dist/core/__tests__/child-death-wake.test.js +56 -111
  119. package/dist/core/__tests__/close.test.js +14 -20
  120. package/dist/core/__tests__/context-intro.test.js +19 -19
  121. package/dist/core/__tests__/daemon-boot.test.js +30 -23
  122. package/dist/core/__tests__/draw-style.test.js +1 -1
  123. package/dist/core/__tests__/error-stall-recycle.test.d.ts +1 -0
  124. package/dist/core/__tests__/error-stall-recycle.test.js +141 -0
  125. package/dist/core/__tests__/fixtures/fake-engine.d.ts +17 -1
  126. package/dist/core/__tests__/fixtures/fake-engine.js +22 -2
  127. package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
  128. package/dist/core/__tests__/focuses.test.js +25 -13
  129. package/dist/core/__tests__/fork.test.js +22 -5
  130. package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +1 -0
  131. package/dist/core/__tests__/full/broker-control-preempt.test.js +61 -0
  132. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +1 -0
  133. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +57 -0
  134. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +1 -0
  135. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +80 -0
  136. package/dist/core/__tests__/full/broker-pane-resolution.test.js +90 -0
  137. package/dist/core/__tests__/full/cascade-close.test.js +22 -8
  138. package/dist/core/__tests__/full/detach-focus.test.js +66 -169
  139. package/dist/core/__tests__/helpers/harness.d.ts +6 -9
  140. package/dist/core/__tests__/helpers/harness.js +19 -36
  141. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +1 -0
  142. package/dist/core/__tests__/human-node-not-supervised.test.js +83 -0
  143. package/dist/core/__tests__/lifecycle.test.js +1 -19
  144. package/dist/core/__tests__/listing-completeness.test.js +2 -2
  145. package/dist/core/__tests__/live-mutation-verbs.test.js +5 -5
  146. package/dist/core/__tests__/memory-resolver.test.js +1 -1
  147. package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -2
  148. package/dist/core/__tests__/refresh-stall-recycle.test.d.ts +1 -0
  149. package/dist/core/__tests__/refresh-stall-recycle.test.js +88 -0
  150. package/dist/core/__tests__/relaunch-root.test.d.ts +1 -0
  151. package/dist/core/__tests__/relaunch-root.test.js +114 -0
  152. package/dist/core/__tests__/reset.test.js +19 -79
  153. package/dist/core/__tests__/steer-note.test.js +9 -7
  154. package/dist/core/__tests__/tmux-surface.test.js +13 -7
  155. package/dist/core/bootstrap.d.ts +0 -3
  156. package/dist/core/bootstrap.js +1 -143
  157. package/dist/core/canvas/browse/__tests__/model.test.js +86 -1
  158. package/dist/core/canvas/browse/__tests__/render.test.js +2 -1
  159. package/dist/core/canvas/browse/app.js +131 -8
  160. package/dist/core/canvas/browse/model.d.ts +23 -5
  161. package/dist/core/canvas/browse/model.js +68 -13
  162. package/dist/core/canvas/browse/render.d.ts +7 -1
  163. package/dist/core/canvas/browse/render.js +178 -47
  164. package/dist/core/canvas/canvas.js +1 -1
  165. package/dist/core/canvas/focuses.d.ts +4 -4
  166. package/dist/core/canvas/focuses.js +7 -7
  167. package/dist/core/canvas/nav-model.js +8 -7
  168. package/dist/core/canvas/render.d.ts +68 -9
  169. package/dist/core/canvas/render.js +195 -61
  170. package/dist/core/canvas/types.d.ts +16 -23
  171. package/dist/core/config.js +1 -4
  172. package/dist/core/memory-resolver.js +43 -5
  173. package/dist/core/runtime/bearings.d.ts +1 -1
  174. package/dist/core/runtime/bearings.js +8 -8
  175. package/dist/core/runtime/branded-host.d.ts +16 -0
  176. package/dist/core/runtime/branded-host.js +127 -0
  177. package/dist/core/runtime/broker-protocol.d.ts +212 -6
  178. package/dist/core/runtime/broker.d.ts +4 -1
  179. package/dist/core/runtime/broker.js +494 -58
  180. package/dist/core/runtime/close.d.ts +12 -2
  181. package/dist/core/runtime/close.js +37 -16
  182. package/dist/core/runtime/front-door.js +6 -14
  183. package/dist/core/runtime/host.d.ts +14 -34
  184. package/dist/core/runtime/host.js +14 -50
  185. package/dist/core/runtime/launch.d.ts +12 -9
  186. package/dist/core/runtime/launch.js +27 -15
  187. package/dist/core/runtime/lifecycle.d.ts +1 -1
  188. package/dist/core/runtime/lifecycle.js +15 -19
  189. package/dist/core/runtime/nodes.d.ts +0 -55
  190. package/dist/core/runtime/nodes.js +6 -74
  191. package/dist/core/runtime/placement.d.ts +91 -335
  192. package/dist/core/runtime/placement.js +291 -858
  193. package/dist/core/runtime/promote.d.ts +2 -0
  194. package/dist/core/runtime/promote.js +24 -6
  195. package/dist/core/runtime/recap.d.ts +8 -0
  196. package/dist/core/runtime/recap.js +107 -0
  197. package/dist/core/runtime/recycle.js +25 -61
  198. package/dist/core/runtime/reset.d.ts +43 -37
  199. package/dist/core/runtime/reset.js +131 -218
  200. package/dist/core/runtime/revive.d.ts +9 -29
  201. package/dist/core/runtime/revive.js +40 -139
  202. package/dist/core/runtime/spawn.d.ts +15 -16
  203. package/dist/core/runtime/spawn.js +165 -194
  204. package/dist/core/runtime/surface-bg.d.ts +11 -0
  205. package/dist/core/runtime/surface-bg.js +68 -0
  206. package/dist/core/runtime/tmux.d.ts +15 -52
  207. package/dist/core/runtime/tmux.js +35 -99
  208. package/dist/core/scope.d.ts +1 -2
  209. package/dist/core/scope.js +1 -5
  210. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts +1 -0
  211. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +57 -0
  212. package/dist/core/skill-sync/builtins.d.ts +42 -0
  213. package/dist/core/skill-sync/builtins.js +112 -0
  214. package/dist/core/skill-sync/claude-plugins.d.ts +23 -0
  215. package/dist/core/skill-sync/claude-plugins.js +71 -0
  216. package/dist/core/skill-sync/engine.d.ts +42 -0
  217. package/dist/core/skill-sync/engine.js +633 -0
  218. package/dist/core/skill-sync/export.d.ts +23 -0
  219. package/dist/core/skill-sync/export.js +86 -0
  220. package/dist/core/skill-sync/manifest.d.ts +64 -0
  221. package/dist/core/skill-sync/manifest.js +181 -0
  222. package/dist/core/skill-sync/profile.d.ts +76 -0
  223. package/dist/core/skill-sync/profile.js +173 -0
  224. package/dist/core/skill-sync/snapshot.d.ts +57 -0
  225. package/dist/core/skill-sync/snapshot.js +120 -0
  226. package/dist/core/substrate/index.d.ts +1 -1
  227. package/dist/core/substrate/index.js +1 -1
  228. package/dist/core/substrate/injected-store.js +3 -3
  229. package/dist/core/substrate/on-read.js +68 -6
  230. package/dist/core/substrate/render.d.ts +8 -11
  231. package/dist/core/substrate/render.js +29 -43
  232. package/dist/core/substrate/schema.d.ts +10 -3
  233. package/dist/core/substrate/schema.js +6 -3
  234. package/dist/core/tui/host.d.ts +12 -4
  235. package/dist/core/tui/host.js +280 -149
  236. package/dist/core/view/bridge.d.ts +10 -0
  237. package/dist/core/view/bridge.js +31 -0
  238. package/dist/core/view/chrome.d.ts +9 -0
  239. package/dist/core/view/chrome.js +22 -0
  240. package/dist/core/view/contract.d.ts +171 -0
  241. package/dist/core/view/contract.js +23 -0
  242. package/dist/core/view/loader.d.ts +31 -0
  243. package/dist/core/view/loader.js +188 -0
  244. package/dist/core/view/transport-local.d.ts +7 -0
  245. package/dist/core/view/transport-local.js +70 -0
  246. package/dist/core/view/transport.d.ts +4 -0
  247. package/dist/core/view/transport.js +15 -0
  248. package/dist/daemon/crtrd.d.ts +38 -27
  249. package/dist/daemon/crtrd.js +303 -376
  250. package/dist/daemon/manage.js +6 -1
  251. package/dist/index.js +1 -1
  252. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +28 -20
  253. package/dist/pi-extensions/canvas-context-intro.js +4 -4
  254. package/dist/pi-extensions/canvas-doc-substrate.js +16 -16
  255. package/dist/pi-extensions/canvas-nav.js +5 -0
  256. package/dist/pi-extensions/canvas-recap.d.ts +37 -0
  257. package/dist/pi-extensions/canvas-recap.js +236 -0
  258. package/dist/pi-extensions/canvas-resume.js +2 -1
  259. package/dist/pi-extensions/canvas-stophook.d.ts +14 -12
  260. package/dist/pi-extensions/canvas-stophook.js +87 -101
  261. package/dist/pi-extensions/canvas-view.js +2 -1
  262. package/dist/pi-extensions/widget-order-bus.d.ts +6 -0
  263. package/dist/pi-extensions/widget-order-bus.js +34 -0
  264. package/dist/prompts/view.d.ts +2 -2
  265. package/dist/prompts/view.js +145 -73
  266. package/dist/types.d.ts +1 -6
  267. package/dist/types.js +1 -3
  268. package/dist/web/ViewChrome.d.ts +7 -0
  269. package/dist/web/ViewChrome.js +28 -0
  270. package/dist/web/ViewPane.d.ts +39 -0
  271. package/dist/web/ViewPane.js +48 -0
  272. package/dist/web/index.d.ts +6 -0
  273. package/dist/web/index.js +16 -0
  274. package/dist/web/runtime.d.ts +39 -0
  275. package/dist/web/runtime.js +133 -0
  276. package/dist/web/states.d.ts +24 -0
  277. package/dist/web/states.js +24 -0
  278. package/dist/web/transport-http.d.ts +5 -0
  279. package/dist/web/transport-http.js +28 -0
  280. package/dist/web-client/assets/index-BUvQb4hR.css +2 -0
  281. package/dist/web-client/assets/index-ClLQXYAE.js +10 -0
  282. package/dist/web-client/index.html +13 -0
  283. package/package.json +20 -6
  284. package/dist/builtin-views/canvas/client.mjs +0 -303
  285. package/dist/builtin-views/canvas/view.mjs +0 -576
  286. package/dist/builtin-views/git-pr/client.mjs +0 -440
  287. package/dist/builtin-views/git-pr/view.mjs +0 -675
  288. package/dist/builtin-views/inbox/sources/gmail.mjs +0 -965
  289. package/dist/builtin-views/inbox/sources/linkedin.mjs +0 -427
  290. package/dist/builtin-views/inbox/view.mjs +0 -889
  291. package/dist/builtin-views/linkedin/client.mjs +0 -610
  292. package/dist/builtin-views/linkedin/view.mjs +0 -1171
  293. package/dist/commands/pkg/bridge.d.ts +0 -1
  294. package/dist/commands/pkg/bridge.js +0 -137
  295. package/dist/commands/skill/author.d.ts +0 -3
  296. package/dist/commands/skill/author.js +0 -140
  297. package/dist/commands/skill/shared.d.ts +0 -3
  298. package/dist/commands/skill/shared.js +0 -19
  299. package/dist/commands/skill.d.ts +0 -2
  300. package/dist/commands/skill.js +0 -21
  301. package/dist/commands/tmux-spread.d.ts +0 -2
  302. package/dist/commands/tmux-spread.js +0 -144
  303. package/dist/core/__tests__/full/daemon-liveness-pane.full.test.js +0 -292
  304. package/dist/core/__tests__/full/placement-focus.test.js +0 -309
  305. package/dist/core/__tests__/full/placement-reconcile.test.js +0 -212
  306. package/dist/core/__tests__/full/placement-revive.test.js +0 -238
  307. package/dist/core/__tests__/full/placement-teardown.test.js +0 -297
  308. package/dist/core/__tests__/home-session.test.js +0 -186
  309. package/dist/core/__tests__/relaunch.test.js +0 -335
  310. package/dist/core/bridge-map.d.ts +0 -19
  311. package/dist/core/bridge-map.js +0 -73
  312. package/dist/core/tui/contract.d.ts +0 -83
  313. package/dist/core/tui/contract.js +0 -8
  314. package/dist/core/tui/loader.d.ts +0 -16
  315. package/dist/core/tui/loader.js +0 -94
  316. package/dist/prompts/skill.d.ts +0 -2
  317. package/dist/prompts/skill.js +0 -650
  318. /package/dist/{core/__tests__/full/daemon-liveness-pane.full.test.d.ts → clients/attach/__tests__/action-parity.test.d.ts} +0 -0
  319. /package/dist/{core/__tests__/full/placement-focus.test.d.ts → clients/attach/__tests__/clipboard-image.test.d.ts} +0 -0
  320. /package/dist/{core/__tests__/full/placement-reconcile.test.d.ts → clients/attach/__tests__/editor-newline.test.d.ts} +0 -0
  321. /package/dist/{core/__tests__/full/placement-revive.test.d.ts → clients/attach/__tests__/git-info.test.d.ts} +0 -0
  322. /package/dist/{core/__tests__/full/placement-teardown.test.d.ts → clients/attach/__tests__/onrequest-wired.test.d.ts} +0 -0
  323. /package/dist/{core/__tests__/home-session.test.d.ts → clients/attach/__tests__/slash-quit-copy.test.d.ts} +0 -0
  324. /package/dist/{core/__tests__/relaunch.test.d.ts → clients/attach/__tests__/titled-editor.test.d.ts} +0 -0
@@ -1,427 +0,0 @@
1
- // @ts-check
2
- /**
3
- * LinkedIn Source adapter for the combined `inbox` view.
4
- *
5
- * A thin adapter over the EXISTING, unchanged `../../linkedin/client.mjs`
6
- * (capture-exec + never-throw `Result` + `classifyError`). It implements the
7
- * `inbox` Source contract: `{ id, label, badge, connectKey, init, ensureReady,
8
- * listRows, loadThread, reply, react, connect }`. The combined view owns the
9
- * merge, the screen, the chrome, and the keymap; THIS module owns LinkedIn's
10
- * data fetching AND its full discover→auth→settle recovery state machine.
11
- *
12
- * The recovery machine (`attempt` / `recover` / `settle`) is PORTED from
13
- * `../../linkedin/view.mjs` into `ensureReady` here — the standalone `linkedin`
14
- * view keeps its own copy untouched. Auto-fix branches drive the browser
15
- * (open/navigate) then bounded-settle-poll; terminal states return a typed
16
- * `SourceError` whose `display` the view renders as this source's banner/panel.
17
- *
18
- * Self-contained ESM, Node-builtins only, imports NOTHING from crtr. NEVER
19
- * throws: every async returns a `Result` (`{ok:true,data}` | `{ok:false,error}`).
20
- *
21
- * @module inbox/sources/linkedin
22
- */
23
-
24
- import {
25
- discoverTab,
26
- getContext,
27
- listConversations,
28
- viewConversation,
29
- sendMessage,
30
- reactToMessage,
31
- openMessagingTab,
32
- navigateToMessaging,
33
- } from '../../linkedin/client.mjs';
34
-
35
- /** @typedef {import('../../linkedin/client.mjs').LiContext} LiContext */
36
- /** @typedef {import('../../linkedin/client.mjs').Conversation} Conversation */
37
- /** @typedef {import('../../linkedin/client.mjs').Message} Message */
38
- /** @typedef {import('../../linkedin/client.mjs').ClientError} ClientError */
39
-
40
- // ── Tunables (match the standalone linkedin view) ────────────────────────────
41
-
42
- /** How many conversations to request per refresh. */
43
- const CONVO_COUNT = 25;
44
-
45
- /** Bounded settle-poll: retry readiness up to N times, spaced ~MS, after an
46
- * auto open/navigate — a hard ceiling so the flow never spins forever. */
47
- const SETTLE_MAX = 5;
48
- const SETTLE_INTERVAL_MS = 1200;
49
-
50
- /** ClientError kinds that waiting can't fix — stop the settle-poll and guide. */
51
- const HARD_STOP = new Set(['no-cdp', 'not-logged-in', 'capture-not-dev']);
52
-
53
- /** @param {number} ms @returns {Promise<void>} */
54
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
55
-
56
- // ── Result helpers (Source contract — distinct from client.mjs's) ────────────
57
-
58
- /** @template T @param {T} [data] @returns {{ok:true, data:T}} */
59
- function ok(data) {
60
- return { ok: true, data: /** @type {any} */ (data) };
61
- }
62
-
63
- /** @param {{kind:string, display:object}} error @returns {{ok:false, error:any}} */
64
- function fail(error) {
65
- return { ok: false, error };
66
- }
67
-
68
- // ── ClientError → SourceError (typed display the view renders) ───────────────
69
-
70
- /**
71
- * @param {string} kind
72
- * @param {{headline:string, explanation?:string|string[], nextStep?:string|null,
73
- * banner:string, level:'info'|'action'|'error', blocking:boolean}} display
74
- * @returns {{kind:string, display:object}}
75
- */
76
- function srcError(kind, display) {
77
- return { kind, display };
78
- }
79
-
80
- /**
81
- * Map a typed {@link ClientError} into a Source `SourceError`. The `banner` text
82
- * is bare (no source label) — the view prefixes it with `LinkedIn: `.
83
- * @param {ClientError} error
84
- * @returns {{kind:string, display:object}}
85
- */
86
- function toSourceError(error) {
87
- const kind = (error && error.kind) || 'error';
88
- switch (kind) {
89
- case 'not-logged-in':
90
- return srcError('not-logged-in', {
91
- headline: 'Log in to continue',
92
- explanation: 'LinkedIn needs a sign-in in the browser.',
93
- nextStep: 'Log in in the opened tab, then press g',
94
- banner: 'log in, then press g',
95
- level: 'action',
96
- blocking: true,
97
- });
98
- case 'no-cdp':
99
- return srcError('no-cdp', {
100
- headline: 'No debuggable browser',
101
- explanation: 'crtr drives a browser over CDP and none is running.',
102
- nextStep: 'Launch Arc, or Chrome with --remote-debugging-port=9222, then press g',
103
- banner: 'no debuggable browser — launch one, then press g',
104
- level: 'error',
105
- blocking: true,
106
- });
107
- case 'rate-limited':
108
- return srcError('rate-limited', {
109
- headline: 'LinkedIn is throttling',
110
- explanation: 'Too many requests — waiting before trying again.',
111
- nextStep: 'Press g to retry',
112
- banner: 'throttling — wait, then press g',
113
- level: 'info',
114
- blocking: false,
115
- });
116
- case 'capture-not-dev':
117
- return srcError('capture-not-dev', {
118
- headline: 'Browser bridge unavailable',
119
- explanation: 'This view needs a capture dev checkout (vault/ + esbuild).',
120
- nextStep: null,
121
- banner: 'browser bridge unavailable — capture dev checkout required',
122
- level: 'error',
123
- blocking: true,
124
- });
125
- case 'not-connection':
126
- return srcError('not-connection', {
127
- headline: 'Not a 1st-degree connection',
128
- explanation: 'LinkedIn only allows messaging 1st-degree connections.',
129
- nextStep: null,
130
- banner: 'can only message 1st-degree connections',
131
- level: 'error',
132
- blocking: false,
133
- });
134
- case 'no-tab':
135
- return srcError('no-tab', {
136
- headline: 'No LinkedIn tab',
137
- explanation: 'No messaging tab is open.',
138
- nextStep: 'Press L to open LinkedIn Messages',
139
- banner: 'no messaging tab — press L to open',
140
- level: 'action',
141
- blocking: true,
142
- });
143
- case 'not-messaging':
144
- return srcError('not-messaging', {
145
- headline: 'LinkedIn is on another page',
146
- explanation: 'A LinkedIn tab is open but not on Messages.',
147
- nextStep: 'Press L to open Messages',
148
- banner: 'on another page — press L for Messages',
149
- level: 'action',
150
- blocking: true,
151
- });
152
- case 'error':
153
- default:
154
- return srcError('error', {
155
- headline: 'Something went wrong',
156
- explanation: (error && /** @type {any} */ (error).message) || 'Unknown error.',
157
- nextStep: 'Press g to retry',
158
- banner: (error && /** @type {any} */ (error).message) || 'something went wrong',
159
- level: 'error',
160
- blocking: true,
161
- });
162
- }
163
- }
164
-
165
- /** Settle-poll exhaustion (transient): keep other rows, nudge a manual retry. */
166
- function settlingError() {
167
- return srcError('settling', {
168
- headline: 'Still loading',
169
- explanation: 'LinkedIn is taking a moment to load.',
170
- nextStep: 'Press g to retry',
171
- banner: 'still loading — press g to retry',
172
- level: 'action',
173
- blocking: false,
174
- });
175
- }
176
-
177
- /**
178
- * Which auto-fix branch a ClientError takes (drive the browser), if any.
179
- * @param {ClientError} error @returns {'open'|'navigate'|null}
180
- */
181
- function autoFixFor(error) {
182
- const k = error && error.kind;
183
- if (k === 'no-tab') return 'open';
184
- if (k === 'not-messaging') return 'navigate';
185
- return null;
186
- }
187
-
188
- // ── Sub (private mutable substate the view stores at state.subs.linkedin) ─────
189
-
190
- /**
191
- * @typedef {Object} LiSub
192
- * @property {LiContext|null} ctx Cached after the first getContext().
193
- * @property {string|null} target Discovered/opened CDP tab id (or options.target).
194
- * @property {string|undefined} port options.port passthrough.
195
- * @property {Conversation[]} convos Last successful inbox fetch (raw order; the view sorts the merge).
196
- * @property {Record<string, Message[]>} threads urn → loaded messages (with urns, for react()).
197
- * @property {boolean} loginTabOpened Once-per-episode login-tab gate; reset on ready.
198
- */
199
-
200
- /** @param {LiSub} sub @returns {{target:string|undefined, port:string|undefined}} */
201
- function baseOpts(sub) {
202
- return { target: sub.target || undefined, port: sub.port };
203
- }
204
-
205
- // ── Readiness state machine (ported from linkedin/view.mjs) ──────────────────
206
-
207
- /**
208
- * One readiness attempt: discover the tab (unless known), read auth context
209
- * once, list conversations (the readiness probe; caches into sub.convos).
210
- * Returns a client-shaped result ({ok:true} or the first ClientError).
211
- * @param {LiSub} sub
212
- * @returns {Promise<{ok:true} | {ok:false, error:ClientError}>}
213
- */
214
- async function attempt(sub) {
215
- if (!sub.target) {
216
- const r = await discoverTab({ port: sub.port });
217
- if (!r.ok) return r;
218
- sub.target = r.data;
219
- }
220
- if (!sub.ctx) {
221
- const r = await getContext(baseOpts(sub));
222
- if (!r.ok) return r;
223
- sub.ctx = r.data;
224
- }
225
- const lc = await listConversations({
226
- ...baseOpts(sub),
227
- csrf: sub.ctx.csrf,
228
- memberId: sub.ctx.memberId,
229
- count: CONVO_COUNT,
230
- });
231
- if (!lc.ok) return lc;
232
- sub.convos = lc.data;
233
- return { ok: true };
234
- }
235
-
236
- /**
237
- * Bounded settle-poll after an auto open/navigate: retry readiness up to
238
- * SETTLE_MAX times spaced SETTLE_INTERVAL_MS. Ready ⇒ ok. Hard-stop ⇒ guided
239
- * SourceError. Exhaustion ⇒ transient settlingError (no infinite spin).
240
- * @param {LiSub} sub
241
- * @returns {Promise<{ok:true}|{ok:false, error:any}>}
242
- */
243
- async function settle(sub) {
244
- for (let i = 1; i <= SETTLE_MAX; i++) {
245
- await sleep(SETTLE_INTERVAL_MS);
246
- const r = await attempt(sub);
247
- if (r.ok) {
248
- sub.loginTabOpened = false;
249
- return ok();
250
- }
251
- if (HARD_STOP.has(r.error.kind)) return fail(toSourceError(r.error));
252
- // transient (no-tab / not-messaging / rate-limited / error) → keep polling
253
- }
254
- return fail(settlingError());
255
- }
256
-
257
- // ── Source contract methods ──────────────────────────────────────────────────
258
-
259
- /**
260
- * Build private substate. Cheap, sync. Seeds target/port from the view options
261
- * (LinkedIn honors host.options.target / host.options.port).
262
- * @param {{options?: Record<string,string>}} host @returns {LiSub}
263
- */
264
- function init(host) {
265
- const opts = (host && host.options) || {};
266
- return {
267
- ctx: null,
268
- target: opts.target || null,
269
- port: opts.port || undefined,
270
- convos: [],
271
- threads: {},
272
- loginTabOpened: false,
273
- };
274
- }
275
-
276
- /**
277
- * Discover → auth → settle, mutating sub in place. Owns the full recovery state
278
- * machine; returns ok when ready, else a SourceError.
279
- * @param {LiSub} sub @param {object} _host
280
- * @returns {Promise<{ok:true}|{ok:false, error:any}>}
281
- */
282
- async function ensureReady(sub, _host) {
283
- const probe = await attempt(sub);
284
- if (probe.ok) {
285
- sub.loginTabOpened = false;
286
- return ok();
287
- }
288
- const error = probe.error;
289
- const auto = autoFixFor(error);
290
- if (auto) {
291
- const fix = auto === 'open'
292
- ? await openMessagingTab({ port: sub.port })
293
- : await navigateToMessaging({ target: sub.target || undefined, port: sub.port });
294
- if (!fix.ok) return fail(toSourceError(fix.error));
295
- if (auto === 'open' && fix.data) sub.target = fix.data;
296
- sub.ctx = null; // re-read auth for the (possibly new) tab/page
297
- return settle(sub);
298
- }
299
- // not-logged-in: open the messaging tab ONCE per episode so the login page is
300
- // visible, then STOP (logged out, /messaging/ redirects to /login, so a new
301
- // tab would spawn on every poll). onReady (probe.ok above) resets the gate.
302
- if (error && error.kind === 'not-logged-in' && !sub.loginTabOpened) {
303
- const o = await openMessagingTab({ port: sub.port });
304
- if (o.ok && o.data) sub.target = o.data;
305
- sub.loginTabOpened = true;
306
- }
307
- return fail(toSourceError(error));
308
- }
309
-
310
- /**
311
- * Map the cached conversations into UnifiedRows. Only invoked after ensureReady
312
- * ok. The view sorts the merged set; we keep raw order here.
313
- * @param {LiSub} sub
314
- * @returns {Promise<{ok:true, data:object[]}>}
315
- */
316
- async function listRows(sub) {
317
- const csrf = sub.ctx ? sub.ctx.csrf : '';
318
- const memberId = sub.ctx ? sub.ctx.memberId : '';
319
- const rows = (sub.convos || []).map((c) => ({
320
- sourceId: 'linkedin',
321
- key: `linkedin:${c.urn}`,
322
- name: c.name || 'Unknown',
323
- snippet: c.lastMessage || '',
324
- unread: !!c.unread,
325
- ts: c.ts || 0,
326
- ref: { urn: c.urn, recipientId: c.recipientId, csrf, memberId },
327
- }));
328
- return ok(rows);
329
- }
330
-
331
- /**
332
- * Load a thread for a row's ref. Caches the raw messages (with urns) so react()
333
- * can target the latest. LinkedIn omits the subtitle.
334
- * @param {LiSub} sub @param {{urn:string, recipientId:string, csrf:string, memberId:string}} ref
335
- * @returns {Promise<{ok:true, data:object}|{ok:false, error:any}>}
336
- */
337
- async function loadThread(sub, ref) {
338
- const csrf = (sub.ctx && sub.ctx.csrf) || ref.csrf;
339
- const memberId = (sub.ctx && sub.ctx.memberId) || ref.memberId;
340
- if (!csrf) return fail(toSourceError({ kind: 'error', message: 'not authenticated yet' }));
341
- const vc = await viewConversation({
342
- ...baseOpts(sub),
343
- csrf,
344
- conversationUrn: ref.urn,
345
- myMemberId: memberId,
346
- });
347
- if (!vc.ok) return fail(toSourceError(vc.error));
348
- sub.threads[ref.urn] = vc.data; // raw messages (urns) for react()
349
- // Optimistic local read-clear (no markConversationAsRead per adapter scope):
350
- // keeps the row visually read until the next server fetch re-lists it.
351
- const c = (sub.convos || []).find((x) => x.urn === ref.urn);
352
- if (c) c.unread = false;
353
- const name = c ? c.name : 'Conversation';
354
- return ok({
355
- title: name || 'Conversation',
356
- messages: vc.data.map((m) => ({ sender: m.sender, fromMe: m.fromMe, text: m.text, ts: m.ts })),
357
- canReply: true,
358
- canReact: true,
359
- });
360
- }
361
-
362
- /**
363
- * Send a reply to the conversation's recipient.
364
- * @param {LiSub} sub @param {{urn:string, recipientId:string, csrf:string, memberId:string}} ref @param {string} text
365
- * @returns {Promise<{ok:true}|{ok:false, error:any}>}
366
- */
367
- async function reply(sub, ref, text) {
368
- const csrf = (sub.ctx && sub.ctx.csrf) || ref.csrf;
369
- const memberId = (sub.ctx && sub.ctx.memberId) || ref.memberId;
370
- if (!csrf) return fail(toSourceError({ kind: 'error', message: 'not authenticated yet' }));
371
- const r = await sendMessage({
372
- ...baseOpts(sub),
373
- csrf,
374
- myMemberId: memberId,
375
- recipient: ref.recipientId,
376
- text,
377
- conversationUrn: ref.urn,
378
- });
379
- if (!r.ok) return fail(toSourceError(r.error));
380
- return ok();
381
- }
382
-
383
- /**
384
- * React to the latest message in the open thread with `emoji`.
385
- * @param {LiSub} sub @param {{urn:string, csrf:string}} ref @param {string} emoji
386
- * @returns {Promise<{ok:true}|{ok:false, error:any}>}
387
- */
388
- async function react(sub, ref, emoji) {
389
- const csrf = (sub.ctx && sub.ctx.csrf) || ref.csrf;
390
- const msgs = sub.threads[ref.urn] || [];
391
- const target = msgs.length ? msgs[msgs.length - 1] : null;
392
- if (!target || !target.urn) return fail(toSourceError({ kind: 'error', message: 'no message to react to' }));
393
- if (!csrf) return fail(toSourceError({ kind: 'error', message: 'not authenticated yet' }));
394
- const r = await reactToMessage({ ...baseOpts(sub), csrf, messageUrn: target.urn, emoji });
395
- if (!r.ok) return fail(toSourceError(r.error));
396
- return ok();
397
- }
398
-
399
- /**
400
- * Manual connect (bound to connectKey 'L'): open/reuse the LinkedIn Messages
401
- * tab; the next refresh re-runs ensureReady against it.
402
- * @param {LiSub} sub @param {object} _host
403
- * @returns {Promise<{ok:true}|{ok:false, error:any}>}
404
- */
405
- async function connect(sub, _host) {
406
- const o = await openMessagingTab({ port: sub.port });
407
- if (!o.ok) return fail(toSourceError(o.error));
408
- if (o.data) sub.target = o.data;
409
- sub.ctx = null;
410
- return ok();
411
- }
412
-
413
- // ── Source object (default export) ───────────────────────────────────────────
414
-
415
- export default {
416
- id: 'linkedin',
417
- label: 'LinkedIn',
418
- badge: { glyph: 'in', fg: '36' },
419
- connectKey: 'L',
420
- init,
421
- ensureReady,
422
- listRows,
423
- loadThread,
424
- reply,
425
- react,
426
- connect,
427
- };