@crouton-kit/crouter 0.3.18 → 0.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-root.js +6 -0
- package/dist/builtin-memory/crouter-development/marketplaces.md +164 -0
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +58 -0
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +60 -0
- package/dist/builtin-memory/crouter-development/personas.md +117 -0
- package/dist/builtin-memory/crouter-development/plugins.md +163 -0
- package/dist/builtin-memory/design.md +60 -0
- package/dist/builtin-memory/development.md +116 -0
- package/dist/builtin-memory/planning.md +66 -0
- package/dist/builtin-memory/spec.md +99 -0
- package/dist/builtin-personas/design/PERSONA.md +1 -0
- package/dist/builtin-personas/design/orchestrator.md +2 -1
- package/dist/builtin-personas/developer/PERSONA.md +1 -0
- package/dist/builtin-personas/developer/orchestrator.md +2 -1
- package/dist/builtin-personas/explore/PERSONA.md +1 -0
- package/dist/builtin-personas/explore/orchestrator.md +4 -0
- package/dist/builtin-personas/general/PERSONA.md +1 -0
- package/dist/builtin-personas/general/orchestrator.md +4 -0
- package/dist/builtin-personas/lifecycle/resident.md +1 -1
- package/dist/builtin-personas/lifecycle/terminal.md +5 -2
- package/dist/builtin-personas/orchestration-kernel.md +15 -24
- package/dist/builtin-personas/plan/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/orchestrator.md +2 -1
- package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -0
- package/dist/builtin-personas/review/PERSONA.md +1 -0
- package/dist/builtin-personas/review/orchestrator.md +4 -0
- package/dist/builtin-personas/runtime-base.md +6 -3
- package/dist/builtin-personas/spec/PERSONA.md +1 -0
- package/dist/builtin-personas/spec/orchestrator.md +2 -1
- package/dist/builtin-personas/waiting.md +8 -0
- package/dist/builtin-skills/skills/crouter-development/personas/orchestrator-prompt/SKILL.md +1 -1
- package/dist/builtin-skills/skills/crouter-development/plugins/SKILL.md +2 -2
- package/dist/builtin-views/_lib/states.mjs +161 -0
- package/dist/builtin-views/canvas/client.mjs +303 -0
- package/dist/builtin-views/canvas/view.mjs +576 -0
- package/dist/builtin-views/git-pr/client.mjs +440 -0
- package/dist/builtin-views/git-pr/view.mjs +675 -0
- package/dist/builtin-views/inbox/_lib/render.mjs +177 -0
- package/dist/builtin-views/inbox/sources/gmail.mjs +965 -0
- package/dist/builtin-views/inbox/sources/linkedin.mjs +427 -0
- package/dist/builtin-views/inbox/view.mjs +889 -0
- package/dist/builtin-views/linkedin/client.mjs +610 -0
- package/dist/builtin-views/linkedin/view.mjs +1171 -0
- package/dist/clients/attach/attach-cmd.d.ts +2 -0
- package/dist/clients/attach/attach-cmd.js +354 -0
- package/dist/clients/attach/chat-view.d.ts +77 -0
- package/dist/clients/attach/chat-view.js +450 -0
- package/dist/clients/attach/clipboard-image.d.ts +16 -0
- package/dist/clients/attach/clipboard-image.js +113 -0
- package/dist/clients/attach/config-load.d.ts +31 -0
- package/dist/clients/attach/config-load.js +113 -0
- package/dist/clients/attach/extension-dialogs.d.ts +29 -0
- package/dist/clients/attach/extension-dialogs.js +101 -0
- package/dist/clients/attach/input-controller.d.ts +54 -0
- package/dist/clients/attach/input-controller.js +204 -0
- package/dist/clients/attach/slash-commands.d.ts +36 -0
- package/dist/clients/attach/slash-commands.js +200 -0
- package/dist/clients/attach/view-socket.d.ts +48 -0
- package/dist/clients/attach/view-socket.js +126 -0
- package/dist/commands/attention.js +3 -3
- package/dist/commands/canvas-prune.js +1 -1
- package/dist/commands/daemon.js +3 -2
- package/dist/commands/human/prompts.js +1 -1
- package/dist/commands/human/queue.js +54 -8
- package/dist/commands/human/shared.d.ts +2 -0
- package/dist/commands/memory/find.d.ts +1 -0
- package/dist/commands/memory/find.js +180 -0
- package/dist/commands/memory/lint.d.ts +1 -0
- package/dist/commands/memory/lint.js +140 -0
- package/dist/commands/memory/list.d.ts +1 -0
- package/dist/commands/memory/list.js +79 -0
- package/dist/commands/memory/read.js +103 -0
- package/dist/commands/memory/shared.d.ts +30 -0
- package/dist/commands/memory/shared.js +122 -0
- package/dist/commands/memory/write.d.ts +1 -0
- package/dist/commands/memory/write.js +85 -0
- package/dist/commands/memory.d.ts +2 -0
- package/dist/commands/memory.js +27 -0
- package/dist/commands/node.d.ts +3 -2
- package/dist/commands/node.js +610 -61
- package/dist/commands/pkg/market-manage.js +1 -1
- package/dist/commands/push.js +6 -6
- package/dist/commands/revive.js +1 -1
- package/dist/commands/skill/author.js +1 -1
- package/dist/commands/skill/shared.d.ts +1 -8
- package/dist/commands/skill/shared.js +2 -55
- package/dist/commands/skill.js +9 -14
- package/dist/commands/sys/doctor.js +1 -1
- package/dist/commands/sys/update.js +1 -1
- package/dist/commands/view-cycle.d.ts +2 -0
- package/dist/commands/view-cycle.js +125 -0
- package/dist/commands/view-list.d.ts +2 -0
- package/dist/commands/view-list.js +66 -0
- package/dist/commands/view-new.d.ts +2 -0
- package/dist/commands/view-new.js +70 -0
- package/dist/commands/view-pick.d.ts +2 -0
- package/dist/commands/view-pick.js +119 -0
- package/dist/commands/view-run.d.ts +2 -0
- package/dist/commands/view-run.js +191 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +29 -0
- package/dist/core/__tests__/broker-attach-limits.test.d.ts +1 -0
- package/dist/core/__tests__/broker-attach-limits.test.js +157 -0
- package/dist/core/__tests__/broker-attach-stream.test.d.ts +1 -0
- package/dist/core/__tests__/broker-attach-stream.test.js +125 -0
- package/dist/core/__tests__/broker-crash-teardown.test.d.ts +1 -0
- package/dist/core/__tests__/broker-crash-teardown.test.js +116 -0
- package/dist/core/__tests__/broker-dialogs.test.d.ts +1 -0
- package/dist/core/__tests__/broker-dialogs.test.js +126 -0
- package/dist/core/__tests__/broker-dormant-wake.test.d.ts +1 -0
- package/dist/core/__tests__/broker-dormant-wake.test.js +51 -0
- package/dist/core/__tests__/broker-lifecycle.test.d.ts +1 -0
- package/dist/core/__tests__/broker-lifecycle.test.js +87 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.d.ts +1 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.js +166 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.d.ts +1 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +102 -0
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +2 -36
- package/dist/core/__tests__/cascade-close.test.js +12 -2
- package/dist/core/__tests__/child-death-wake.test.d.ts +1 -0
- package/dist/core/__tests__/child-death-wake.test.js +245 -0
- package/dist/core/__tests__/context-intro.test.js +76 -62
- package/dist/core/__tests__/daemon-boot.test.js +14 -5
- package/dist/core/__tests__/daemon-liveness.test.js +34 -9
- package/dist/core/__tests__/detach-focus.test.d.ts +1 -0
- package/dist/core/__tests__/detach-focus.test.js +206 -0
- package/dist/core/__tests__/draw-style.test.d.ts +1 -0
- package/dist/core/__tests__/draw-style.test.js +258 -0
- package/dist/core/__tests__/fixtures/c3-custom-provider-ext.d.ts +2 -0
- package/dist/core/__tests__/fixtures/c3-custom-provider-ext.js +18 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +138 -0
- package/dist/core/__tests__/fixtures/fake-engine.js +614 -0
- package/dist/core/__tests__/fixtures/fake-pi-host.js +1 -0
- package/dist/core/__tests__/flagship-lifecycle.test.js +7 -1
- package/dist/core/__tests__/frame-decoder-perf.test.d.ts +1 -0
- package/dist/core/__tests__/frame-decoder-perf.test.js +198 -0
- package/dist/core/__tests__/helpers/broker-clients.d.ts +43 -0
- package/dist/core/__tests__/helpers/broker-clients.js +178 -0
- package/dist/core/__tests__/helpers/harness.d.ts +9 -0
- package/dist/core/__tests__/helpers/harness.js +111 -1
- package/dist/core/__tests__/home-session.test.js +41 -8
- package/dist/core/__tests__/human-new-window-regression.test.d.ts +1 -0
- package/dist/core/__tests__/human-new-window-regression.test.js +101 -0
- package/dist/core/__tests__/kickoff.test.js +37 -3
- package/dist/core/__tests__/live-mutation-verbs.test.d.ts +1 -0
- package/dist/core/__tests__/live-mutation-verbs.test.js +175 -0
- package/dist/core/__tests__/live-mutation.test.js +6 -132
- package/dist/core/__tests__/memory.test.js +23 -115
- package/dist/core/__tests__/placement-focus.test.js +5 -0
- package/dist/core/__tests__/placement-teardown.test.js +54 -11
- package/dist/core/__tests__/relaunch.test.js +4 -3
- package/dist/core/__tests__/review-render-pane-regression.test.d.ts +1 -0
- package/dist/core/__tests__/review-render-pane-regression.test.js +133 -0
- package/dist/core/__tests__/spawn-root.test.js +10 -0
- package/dist/core/__tests__/spike-harness.test.js +1 -0
- package/dist/core/__tests__/wake-bearings.test.d.ts +1 -0
- package/dist/core/__tests__/wake-bearings.test.js +156 -0
- package/dist/core/__tests__/wake-origin.test.d.ts +1 -0
- package/dist/core/__tests__/wake-origin.test.js +110 -0
- package/dist/core/bootstrap.js +1 -1
- package/dist/core/canvas/browse/__tests__/render.test.js +1 -0
- package/dist/core/canvas/browse/app.js +24 -2
- package/dist/core/canvas/browse/model.d.ts +38 -2
- package/dist/core/canvas/browse/model.js +134 -10
- package/dist/core/canvas/browse/render.d.ts +6 -12
- package/dist/core/canvas/browse/render.js +72 -104
- package/dist/core/canvas/canvas.js +16 -23
- package/dist/core/canvas/db.js +46 -0
- package/dist/core/canvas/index.d.ts +1 -0
- package/dist/core/canvas/index.js +1 -0
- package/dist/core/canvas/pid.d.ts +4 -0
- package/dist/core/canvas/pid.js +23 -0
- package/dist/core/canvas/render.d.ts +11 -2
- package/dist/core/canvas/render.js +69 -0
- package/dist/core/canvas/types.d.ts +91 -3
- package/dist/core/canvas/wakeups.d.ts +76 -0
- package/dist/core/canvas/wakeups.js +185 -0
- package/dist/core/config.js +4 -1
- package/dist/core/frontmatter.js +37 -124
- package/dist/core/memory-resolver.d.ts +49 -0
- package/dist/core/memory-resolver.js +141 -0
- package/dist/core/personas/loader.d.ts +7 -0
- package/dist/core/personas/loader.js +36 -5
- package/dist/core/personas/resolve.js +13 -3
- package/dist/core/predicate.d.ts +63 -0
- package/dist/core/predicate.js +189 -0
- package/dist/core/resolver.js +26 -5
- package/dist/core/runtime/bearings.d.ts +53 -12
- package/dist/core/runtime/bearings.js +132 -59
- package/dist/core/runtime/broker-cli.d.ts +1 -0
- package/dist/core/runtime/broker-cli.js +46 -0
- package/dist/core/runtime/broker-protocol.d.ts +332 -0
- package/dist/core/runtime/broker-protocol.js +153 -0
- package/dist/core/runtime/broker-sdk.d.ts +48 -0
- package/dist/core/runtime/broker-sdk.js +72 -0
- package/dist/core/runtime/broker.d.ts +55 -0
- package/dist/core/runtime/broker.js +1128 -0
- package/dist/core/runtime/close.js +35 -6
- package/dist/core/runtime/host.d.ts +53 -0
- package/dist/core/runtime/host.js +186 -0
- package/dist/core/runtime/kickoff.d.ts +2 -1
- package/dist/core/runtime/kickoff.js +91 -5
- package/dist/core/runtime/launch.d.ts +54 -4
- package/dist/core/runtime/launch.js +85 -5
- package/dist/core/runtime/lifecycle.js +23 -6
- package/dist/core/runtime/memory.d.ts +2 -42
- package/dist/core/runtime/memory.js +11 -162
- package/dist/core/runtime/nodes.d.ts +37 -0
- package/dist/core/runtime/nodes.js +60 -1
- package/dist/core/runtime/persona.js +21 -11
- package/dist/core/runtime/pi-vendored.d.ts +18 -0
- package/dist/core/runtime/pi-vendored.js +49 -0
- package/dist/core/runtime/placement.d.ts +32 -14
- package/dist/core/runtime/placement.js +206 -52
- package/dist/core/runtime/promote.d.ts +0 -6
- package/dist/core/runtime/promote.js +4 -12
- package/dist/core/runtime/{demote.d.ts → recycle.d.ts} +5 -5
- package/dist/core/runtime/{demote.js → recycle.js} +27 -11
- package/dist/core/runtime/reset.js +11 -8
- package/dist/core/runtime/revive.d.ts +2 -0
- package/dist/core/runtime/revive.js +34 -34
- package/dist/core/runtime/spawn.d.ts +23 -0
- package/dist/core/runtime/spawn.js +77 -23
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +6 -1
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +1 -1
- package/dist/core/runtime/tmux.d.ts +28 -0
- package/dist/core/runtime/tmux.js +80 -6
- package/dist/core/scope.d.ts +11 -0
- package/dist/core/scope.js +39 -0
- package/dist/core/spawn.d.ts +5 -0
- package/dist/core/spawn.js +20 -2
- package/dist/core/substrate/gate.d.ts +13 -0
- package/dist/core/substrate/gate.js +21 -0
- package/dist/core/substrate/index.d.ts +7 -0
- package/dist/core/substrate/index.js +18 -0
- package/dist/core/substrate/on-read.d.ts +14 -0
- package/dist/core/substrate/on-read.js +292 -0
- package/dist/core/substrate/render.d.ts +25 -0
- package/dist/core/substrate/render.js +256 -0
- package/dist/core/substrate/schema.d.ts +76 -0
- package/dist/core/substrate/schema.js +124 -0
- package/dist/core/substrate/session-cache.d.ts +30 -0
- package/dist/core/substrate/session-cache.js +77 -0
- package/dist/core/substrate/subject.d.ts +41 -0
- package/dist/core/substrate/subject.js +54 -0
- package/dist/core/tui/contract.d.ts +83 -0
- package/dist/core/tui/contract.js +8 -0
- package/dist/core/tui/draw.d.ts +96 -0
- package/dist/core/tui/draw.js +339 -0
- package/dist/core/tui/host.d.ts +29 -0
- package/dist/core/tui/host.js +379 -0
- package/dist/core/tui/loader.d.ts +16 -0
- package/dist/core/tui/loader.js +94 -0
- package/dist/core/{canvas/browse → tui}/terminal.js +7 -6
- package/dist/core/wake.d.ts +86 -0
- package/dist/core/wake.js +308 -0
- package/dist/daemon/crtrd.d.ts +29 -4
- package/dist/daemon/crtrd.js +662 -46
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +171 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +19 -12
- package/dist/pi-extensions/canvas-commands.d.ts +3 -0
- package/dist/pi-extensions/canvas-commands.js +10 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +17 -0
- package/dist/pi-extensions/canvas-context-intro.js +55 -15
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +44 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +112 -0
- package/dist/pi-extensions/canvas-nav.d.ts +3 -0
- package/dist/pi-extensions/canvas-nav.js +87 -23
- package/dist/pi-extensions/canvas-stophook.js +17 -8
- package/dist/pi-extensions/canvas-view.d.ts +21 -0
- package/dist/pi-extensions/canvas-view.js +75 -0
- package/dist/prompts/skill.js +19 -26
- package/dist/prompts/view.d.ts +7 -0
- package/dist/prompts/view.js +101 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -0
- package/package.json +9 -4
- package/dist/commands/skill/find.d.ts +0 -4
- package/dist/commands/skill/find.js +0 -257
- package/dist/commands/skill/read.js +0 -91
- /package/dist/commands/{skill → memory}/read.d.ts +0 -0
- /package/dist/core/{canvas/browse → tui}/terminal.d.ts +0 -0
|
@@ -0,0 +1,427 @@
|
|
|
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
|
+
};
|