@hraness/ghostget 0.18.0 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +283 -0
- package/README.md +7 -7
- package/dist/apple-photos-client.js +2 -1
- package/dist/beeper-client.js +2 -1
- package/dist/client.js +1 -0
- package/dist/imessage-direct-install-2gm3kge7.js +324 -0
- package/dist/index-01eeae9e.js +5 -0
- package/dist/index-2ymnp8xv.js +145 -0
- package/dist/index-en5hycxp.js +175 -0
- package/dist/{index-9wca02er.js → index-hfbygww8.js} +1 -1
- package/dist/index-n4szk3nw.js +50 -0
- package/dist/index-yq6maz71.js +1105 -0
- package/dist/index-z1w83f81.js +4 -0
- package/dist/index.js +4 -47
- package/dist/messaging-automation-api.js +21 -0
- package/dist/messaging-automation-j4274hvc.js +609 -0
- package/dist/messaging-native-install-8w1j36ah.js +16 -0
- package/dist/messaging.js +1 -0
- package/dist/omni-client.js +1 -0
- package/dist/whatsapp-automation-runtime-hgh1e9rm.js +845 -0
- package/dist/whatsapp-client.js +1 -0
- package/docs/control-panel.md +2 -2
- package/docs/imessage-direct-provider.md +31 -7
- package/docs/messaging-automation.md +103 -0
- package/package.json +22 -2
- package/skills/ghostget/references/control-panel.md +1 -1
- package/skills/ghostget/references/install.md +5 -5
- package/skills/ghostget/references/linkedin-adapter.md +209 -13
- package/skills/ghostget/references/platform-patterns.md +1 -1
- package/src/args.ts +18 -5
- package/src/assets/adapters/imessage/wrench-web-adapter.json +126 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.json +1 -1
- package/src/assets/adapters/whatsapp/wrench-web-adapter.json +144 -0
- package/src/assets/messaging-runtime/NOTICE.txt +2580 -0
- package/src/assets/messaging-runtime/imsg-darwin-arm64.gz +0 -0
- package/src/assets/messaging-runtime/phone-number-info.plist.gz +0 -0
- package/src/assets/messaging-runtime/phone-number-metadata.json.gz +0 -0
- package/src/assets/messaging-runtime/phone-number-privacy.plist.gz +0 -0
- package/src/assets/messaging-runtime/wacli-darwin-arm64.gz +0 -0
- package/src/beeper-client-types.ts +1 -1
- package/src/ghostget.ts +16 -4
- package/src/messaging-automation-api.ts +12 -0
- package/src/messaging-automation-descriptors.ts +29 -0
- package/src/messaging-automation-factory.ts +148 -0
- package/src/messaging-automation-server.ts +183 -0
- package/src/messaging-automation-types.ts +155 -0
- package/src/messaging-automation-validation.ts +110 -0
- package/src/messaging-automation.ts +356 -0
- package/src/plugins/imessage-direct/plugin.ts +12 -1
- package/src/plugins/imessage-direct/vendor/0003-feat-rpc-add-no-fetch-rich-cards.patch +276 -0
- package/src/plugins/imessage-direct/vendor/provenance.json +84 -10
- package/src/plugins/linkedin-web/plugin.ts +1 -1
- package/src/plugins/whatsapp-linked-device/plugin.ts +7 -2
- package/src/plugins/whatsapp-linked-device/vendor/0001-ghostget-private-messaging.patch +1093 -0
- package/src/plugins/whatsapp-linked-device/vendor/README.md +39 -0
- package/src/plugins/whatsapp-linked-device/vendor/provenance.json +45 -0
- package/src/provider-plugin.ts +14 -0
- package/src/providers/imessage-automation.ts +250 -0
- package/src/providers/imessage-direct-install.ts +7 -0
- package/src/providers/imessage-direct-runtime.ts +32 -2
- package/src/providers/imessage-direct.ts +13 -3
- package/src/providers/linkedin-contact-failure.ts +21 -0
- package/src/providers/linkedin-contact-platform.ts +8 -1
- package/src/providers/linkedin-contact-program.ts +34 -5
- package/src/providers/linkedin-web-contact.ts +1023 -51
- package/src/providers/linkedin-web-profile-browser.ts +659 -45
- package/src/providers/linkedin-web-runtime.ts +1 -0
- package/src/providers/linkedin-web.ts +46 -2
- package/src/providers/messaging-native-artifacts.ts +38 -0
- package/src/providers/messaging-native-install.ts +75 -0
- package/src/providers/whatsapp-automation-runtime.ts +240 -0
- package/src/providers/whatsapp-automation.ts +153 -0
- package/src/usage.ts +3 -1
- package/src/version.ts +1 -1
- package/src/web-session-contract-definitions.ts +1 -1
- package/tsconfig.json +3 -0
package/dist/whatsapp-client.js
CHANGED
package/docs/control-panel.md
CHANGED
|
@@ -9,7 +9,7 @@ and capabilities refresh when you return to the app, after changes made in the
|
|
|
9
9
|
app, or when you choose Refresh. Permission decisions always validate current
|
|
10
10
|
account and integration state.
|
|
11
11
|
|
|
12
|
-
Build instructions and native qualification live in [desktop/README.md](https://github.com/hraness/ghostget/blob/v0.18.
|
|
12
|
+
Build instructions and native qualification live in [desktop/README.md](https://github.com/hraness/ghostget/blob/v0.18.2/desktop/README.md).
|
|
13
13
|
The CLI's canonical five-file GitHub Release contract is unchanged. A source
|
|
14
14
|
build is not a signed or notarized public macOS installer.
|
|
15
15
|
|
|
@@ -123,7 +123,7 @@ User and imported interfaces are distinguished from bundled interfaces. Remote
|
|
|
123
123
|
references, executable import hooks, arbitrary HTTP templates, and unsupported
|
|
124
124
|
input-schema constructs are rejected or remain visibly inert as appropriate.
|
|
125
125
|
An interface without an executor does not become executable by importing it.
|
|
126
|
-
Use the existing [provider plugin protocol](https://github.com/hraness/ghostget/blob/v0.18.
|
|
126
|
+
Use the existing [provider plugin protocol](https://github.com/hraness/ghostget/blob/v0.18.2/docs/plugins.md) when a new executor is
|
|
127
127
|
needed. OpenAPI import does not grant it account access.
|
|
128
128
|
|
|
129
129
|
Middleware and LLM approval are future extensions. Future hooks may propose
|
|
@@ -19,7 +19,12 @@ The vendored patch stack applies to `openclaw/imsg` 0.14.1 at commit
|
|
|
19
19
|
`292db82d89293867ef847a2875667fea0fdd5dc1`, isolates AppleScript payloads.
|
|
20
20
|
The second, `c5994f00d17969fd7772fd2772e7b3591089513a`, adds the read-only exact
|
|
21
21
|
`chats.get(chat_id)` lookup required to revalidate a route without relying on a
|
|
22
|
-
bounded recent-chat scan.
|
|
22
|
+
bounded recent-chat scan. The third, `520b82ab025c1d4d57333b552aa65c9ae3fdb5fd`,
|
|
23
|
+
adds `send.rich` URL cards with `fetch_metadata: false`. The admitted `.3`
|
|
24
|
+
executable includes this mode. It bypasses the helper's LinkPresentation metadata
|
|
25
|
+
and image fetching while retaining the native rich-card payload. The patch also
|
|
26
|
+
corrects exact-chat test fixtures and requires the shipped PhoneNumberKit resource
|
|
27
|
+
bundle in release builds. Patch SHA-256 values, changed files, and the full
|
|
23
28
|
artifact review boundary are recorded in
|
|
24
29
|
`src/plugins/imessage-direct/vendor/provenance.json`.
|
|
25
30
|
|
|
@@ -47,25 +52,44 @@ usable, not which Apple account Messages will choose.
|
|
|
47
52
|
|
|
48
53
|
## Build and installation boundary
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
The admitted binary includes all three vendored mail patches applied to the exact base,
|
|
56
|
+
and was built in release mode. The current reviewed macOS arm64 executable
|
|
52
57
|
SHA-256 is
|
|
53
|
-
`
|
|
54
|
-
|
|
58
|
+
`46c4c73c81c7db2d516c2d467c66aff03c73de196996d646bc8afce0ea85cff6`.
|
|
59
|
+
Two independent release builds produce identical signed bytes with `-Xlinker -S`.
|
|
60
|
+
This omits debug build paths before the default UUID and ad-hoc signature are
|
|
61
|
+
generated. The adjacent resource bundle was exercised with the release
|
|
62
|
+
PhoneNumberKit code. Compiler, SDK, and command provenance are in the manifest. A different byte is
|
|
55
63
|
not this reviewed transport, even if it prints version 0.14.1.
|
|
56
64
|
|
|
57
65
|
Install only that byte sequence through the checked installer:
|
|
58
66
|
|
|
59
67
|
```sh
|
|
60
|
-
ghostget imessage transport install --
|
|
68
|
+
ghostget imessage transport install --json
|
|
61
69
|
```
|
|
62
70
|
|
|
63
|
-
The
|
|
71
|
+
The default uses the exact compressed executable and PhoneNumberKit resources
|
|
72
|
+
bundled with Ghostget. An explicit `--binary /absolute/path/to/imsg` may supply
|
|
73
|
+
the same pinned executable. The installer supports only the declared current platform, reads the source
|
|
64
74
|
without following a symlink, checks owner, mode, size, stability, and SHA-256,
|
|
65
75
|
and installs by an exclusive same-filesystem link. It never replaces mismatched
|
|
66
76
|
existing bytes. Software build, installation, Messages login, and account
|
|
67
77
|
recovery remain operator workflows, not provider operations.
|
|
68
78
|
|
|
79
|
+
The separate [owner messaging host](messaging-automation.md) supports native
|
|
80
|
+
reactions, stickers and polls only when the existing compatible
|
|
81
|
+
Messages bridge reports those operations available. Upstream's injected bridge
|
|
82
|
+
requires a SIP-disabled Mac. Installing Ghostget's CLI binary does not install
|
|
83
|
+
or inject that bridge, disable SIP, or relaunch Messages. A normal Mac without
|
|
84
|
+
that explicit setup can still use the reviewed AppleScript text and attachment
|
|
85
|
+
path after the required macOS permissions are granted.
|
|
86
|
+
|
|
87
|
+
Native rich cards require the compatible bridge to report `send.rich` available.
|
|
88
|
+
The messaging-host path requests `fetch_metadata: false` for those cards.
|
|
89
|
+
The card contains the URL and its host name, without a fetched title or preview
|
|
90
|
+
image. This confines this helper's metadata preparation; it does not claim to
|
|
91
|
+
control all network behavior inside Messages or on the recipient's device.
|
|
92
|
+
|
|
69
93
|
Bind the local Messages store after the reviewed binary is installed:
|
|
70
94
|
|
|
71
95
|
```sh
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Owner messaging host
|
|
2
|
+
|
|
3
|
+
`ghostget messaging automation serve --stdio` is the trusted owner control port
|
|
4
|
+
for enrolled iMessage and WhatsApp conversations. It is never an agent tool.
|
|
5
|
+
All configuration, targets, message bodies and assets arrive on stdin; stdout
|
|
6
|
+
contains only protocol responses. Do not run it in a terminal or record its
|
|
7
|
+
streams in general logs. It does not pair accounts or start synchronization on
|
|
8
|
+
initialization.
|
|
9
|
+
|
|
10
|
+
The `@hraness/ghostget/messaging-automation` SDK exports the closed message,
|
|
11
|
+
grant, plan and receipt types, an explicit `createMessagingAutomationHost` for
|
|
12
|
+
trusted owner-supplied providers, and `installBundledMessagingRuntime`. Imports
|
|
13
|
+
are inert in Node and Bun; host construction requires Bun. Supplied providers
|
|
14
|
+
must enforce their own exact permissions and cleanup. Use the stdio host above
|
|
15
|
+
for Ghostget's built-in accounts and managed registry; its session factory is
|
|
16
|
+
internal and is not part of the SDK.
|
|
17
|
+
|
|
18
|
+
Install the current bundled `imessage-direct` or `whatsapp-web` adapter, bind an
|
|
19
|
+
explicit existing Ghostget account, and enable managed operation permissions in
|
|
20
|
+
Ghostget. Grant `allow` separately to `messaging.automation.read`, the required
|
|
21
|
+
`messaging.automation.send.<kind>` operations, and (WhatsApp only)
|
|
22
|
+
`messaging.automation.sync`. An old `messaging.send` allow does not authorize this
|
|
23
|
+
host. `ask`, `deny`, and an unmanaged policy do not provide unattended authority.
|
|
24
|
+
Updated manifests and automatic implementation closures invalidate old grants.
|
|
25
|
+
These catalog entries cannot dispatch through generic `invoke` or `confirm`.
|
|
26
|
+
|
|
27
|
+
The private transport binaries are separate owner setup:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
ghostget imessage transport install --json
|
|
31
|
+
ghostget whatsapp automation install --json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The default installs the exact compressed runtime included in the Ghostget package. An optional `--binary /absolute/reviewed-file` selects an explicit source instead. Only exact pinned bytes are accepted. Installation starts no helper. The
|
|
35
|
+
WhatsApp binary is the reviewed wacli build with durable events and exact action
|
|
36
|
+
claims; a stock wacli does not qualify. Existing account pairing and macOS
|
|
37
|
+
permissions remain Ghostget setup responsibilities.
|
|
38
|
+
|
|
39
|
+
Each newline-delimited JSON request is `{protocol,id,method,params}`, with
|
|
40
|
+
`protocol` exactly `ghostget.messaging-automation/1`. IDs are distinct active
|
|
41
|
+
ASCII identifiers of at most 64 bytes. Successful responses contain
|
|
42
|
+
`{protocol,id,ok:true,result}`; failures contain a bounded generic
|
|
43
|
+
`{protocol,id,ok:false,error:{code,message}}`. No diagnostic contains credentials,
|
|
44
|
+
provider state paths, or message contents. Clients must correlate IDs: priority
|
|
45
|
+
responses can arrive before a pending ordinary response.
|
|
46
|
+
|
|
47
|
+
| Method | Exact params | Result |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| initialize | providers: 1–2 `{provider,authId}`, distinct networks | `{initialized:true}` |
|
|
50
|
+
| status | provider | Current identity and permissions-intersected capabilities |
|
|
51
|
+
| start | provider | Current status; WhatsApp sync starts explicitly |
|
|
52
|
+
| conversations | provider, limit (1–200) | Current individual conversations |
|
|
53
|
+
| enroll | provider, coordinate | Exact identity/participants enrollment and historical baseline |
|
|
54
|
+
| enrollments | empty object | Persisted enrollments |
|
|
55
|
+
| grant | intentId, enrollmentId, expectedBindingDigest, actions, expiresAt, maximumActions, minimumIntervalMs | Bounded owner grant |
|
|
56
|
+
| grant.by-intent | intentId | `{grant}`; null only when no matching durable grant exists |
|
|
57
|
+
| grant.get | grantId | Current grant expiry, revocation and consumed quota |
|
|
58
|
+
| revoke | grantId | `{revoked:true}` |
|
|
59
|
+
| poll | enrollmentId | Refresh and admit new events; gaps remain explicit |
|
|
60
|
+
| history | enrollmentId, limit (1–200) | Stored admitted history and enrollment; no additional provider read |
|
|
61
|
+
| events | enrollmentIds, cursor (nullable), limit (1–500) | Durable scoped event page |
|
|
62
|
+
| asset | bytesBase64, sha256 | assetId, byte count, digest, expiry |
|
|
63
|
+
| prepare | enrollmentId, expectedRevision, intentId, actions | Exact two-minute plan |
|
|
64
|
+
| submit | planId, grantId | Durable terminal/uncertain action receipt |
|
|
65
|
+
| cancel | planId | `{cancelled:boolean}`; join submit to learn its outcome |
|
|
66
|
+
| run | runId | Retained exact run receipt |
|
|
67
|
+
| close | empty object | `{closed:true}` only after cleanup joins |
|
|
68
|
+
|
|
69
|
+
Ordinary requests are serialized by the client; the server rejects a second
|
|
70
|
+
ordinary in-flight request. Cancel, revoke and close have a separate capacity of
|
|
71
|
+
eight and may interrupt a pending submit. Cancellation never proves that an
|
|
72
|
+
already-started action was unsent. Grant creation requires an owner-persisted intent ID; recover an uncertain response with `grant.by-intent` before doing anything else. Keep reading the original submit response,
|
|
73
|
+
and reconcile its exact run identity without resubmitting uncertain work.
|
|
74
|
+
|
|
75
|
+
Frames are bounded to 24 MiB; responses to 32 MiB. Assets use canonical Base64 and
|
|
76
|
+
a checked SHA-256, at most 16 MiB each, 64 MiB total and 32 entries. An unclaimed
|
|
77
|
+
asset lasts five minutes; prepare binds it to one plan's expiry, and only that
|
|
78
|
+
submit can resolve its bytes. Terminal submit removes its assets. Assets do not
|
|
79
|
+
survive restart and never accept arbitrary filesystem paths.
|
|
80
|
+
|
|
81
|
+
Every provider operation rechecks the account incarnation, exact implementation
|
|
82
|
+
identity and managed permission. Explicit sync keeps a durable cleanup admission
|
|
83
|
+
for the lifetime of the owned WhatsApp process; loss or revocation of its sync
|
|
84
|
+
allow closes the provider, checked at one-second intervals. iMessage operations
|
|
85
|
+
join and release their own admissions. A cleanup failure retains the existing
|
|
86
|
+
Ghostget recovery marker across restart. Never remove it or take over an unknown
|
|
87
|
+
socket merely because the parent process exited. The owner must use the existing
|
|
88
|
+
Ghostget recovery process to establish exact descendant and resource cleanup.
|
|
89
|
+
|
|
90
|
+
The host stores enrollments, grants, historical baselines, durable cursors and
|
|
91
|
+
action claims privately in the selected Ghostget state home. Source generation,
|
|
92
|
+
account or participant drift prevents continuation. Historical bootstrap does
|
|
93
|
+
not become a live event; possible gaps prevent automatic sends. Provider
|
|
94
|
+
acceptance is reported separately from delivery or read status. Available rich
|
|
95
|
+
actions depend on the current pinned helper and explicit permissions;
|
|
96
|
+
app-clips and arbitrary mini-app experiences remain unavailable.
|
|
97
|
+
|
|
98
|
+
Native iMessage links use the pinned `.3` helper and require the compatible
|
|
99
|
+
bridge to report `send.rich` available. The host sets `fetch_metadata: false`
|
|
100
|
+
to build a native URL/host-title card without helper metadata or image fetching.
|
|
101
|
+
This does not control all network behavior inside Messages or on the recipient
|
|
102
|
+
device. WhatsApp link sharing uses its existing text payload with preview
|
|
103
|
+
fetching disabled.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hraness/ghostget",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "Open-source CLI and TypeScript SDK for precise web capabilities for AI agents: page capture, verified media archives, encrypted reads, and typed provider operations.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -74,6 +74,10 @@
|
|
|
74
74
|
"./messaging": {
|
|
75
75
|
"types": "./src/messaging-types.ts",
|
|
76
76
|
"import": "./dist/messaging.js"
|
|
77
|
+
},
|
|
78
|
+
"./messaging-automation": {
|
|
79
|
+
"types": "./src/messaging-automation-types.ts",
|
|
80
|
+
"import": "./dist/messaging-automation-api.js"
|
|
77
81
|
}
|
|
78
82
|
},
|
|
79
83
|
"bin": {
|
|
@@ -315,6 +319,21 @@
|
|
|
315
319
|
"src/message-like-me-agentic-messaging.ts",
|
|
316
320
|
"src/messaging-types.ts",
|
|
317
321
|
"src/messaging.ts",
|
|
322
|
+
"src/messaging-automation-api.ts",
|
|
323
|
+
"src/messaging-automation-types.ts",
|
|
324
|
+
"src/messaging-automation-validation.ts",
|
|
325
|
+
"src/messaging-automation.ts",
|
|
326
|
+
"src/messaging-automation-descriptors.ts",
|
|
327
|
+
"src/messaging-automation-factory.ts",
|
|
328
|
+
"src/messaging-automation-server.ts",
|
|
329
|
+
"src/providers/imessage-automation.ts",
|
|
330
|
+
"src/providers/whatsapp-automation.ts",
|
|
331
|
+
"src/providers/whatsapp-automation-runtime.ts",
|
|
332
|
+
"src/providers/messaging-native-install.ts",
|
|
333
|
+
"src/providers/messaging-native-artifacts.ts",
|
|
334
|
+
"src/assets/messaging-runtime",
|
|
335
|
+
"src/plugins/whatsapp-linked-device/vendor",
|
|
336
|
+
"docs/messaging-automation.md",
|
|
318
337
|
"src/messaging-action-store.ts",
|
|
319
338
|
"src/messaging-runtime.ts",
|
|
320
339
|
"src/messaging-store.ts",
|
|
@@ -348,6 +367,7 @@
|
|
|
348
367
|
"src/plugins/imessage-direct/plugin.ts",
|
|
349
368
|
"src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch",
|
|
350
369
|
"src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch",
|
|
370
|
+
"src/plugins/imessage-direct/vendor/0003-feat-rpc-add-no-fetch-rich-cards.patch",
|
|
351
371
|
"src/plugins/imessage-direct/vendor/provenance.json",
|
|
352
372
|
"src/plugins/linkedin-official/plugin.ts",
|
|
353
373
|
"src/plugins/linkedin-web/plugin.ts",
|
|
@@ -573,7 +593,7 @@
|
|
|
573
593
|
"website:test": "bun test ./website ./edge",
|
|
574
594
|
"website:typecheck": "tsc --noEmit --project website/tsconfig.json && tsc --noEmit --project edge/tsconfig.json",
|
|
575
595
|
"website:check": "bun run check:theme && bun run website:typecheck && bun run website:test && bun run website:build",
|
|
576
|
-
"build": "bun -e 'await (await import(\"node:fs/promises\")).rm(\"./dist\", { recursive: true, force: true })' && bun build ./src/index.ts ./src/client.ts ./src/beeper-client.ts ./src/apple-photos-client.ts ./src/whatsapp-client.ts ./src/omni-client.ts ./src/messaging.ts --outdir ./dist --root ./src --target bun --format esm --splitting --packages external",
|
|
596
|
+
"build": "bun -e 'await (await import(\"node:fs/promises\")).rm(\"./dist\", { recursive: true, force: true })' && bun build ./src/index.ts ./src/client.ts ./src/beeper-client.ts ./src/apple-photos-client.ts ./src/whatsapp-client.ts ./src/omni-client.ts ./src/messaging.ts ./src/messaging-automation-api.ts --outdir ./dist --root ./src --target bun --format esm --splitting --packages external",
|
|
577
597
|
"typecheck": "tsc --noEmit",
|
|
578
598
|
"whatsapp:transport:install": "sh src/scripts/install-whatsapp-protocol.sh",
|
|
579
599
|
"test:unit": "bun test --no-orphans --timeout 180000 --max-concurrency \"${GOMAXPROCS:-4}\" ./src --path-ignore-patterns='**/src/omni-runtime.test.ts'",
|
|
@@ -40,4 +40,4 @@ Unsupported executors remain inert. Follow the existing provider-plugin
|
|
|
40
40
|
authoring protocol to add an executor; do not synthesize arbitrary authenticated
|
|
41
41
|
HTTP or broaden an existing grant. Interface changes invalidate exact grants.
|
|
42
42
|
|
|
43
|
-
The product reference is the [Ghostget control panel guide](https://github.com/hraness/ghostget/blob/v0.18.
|
|
43
|
+
The product reference is the [Ghostget control panel guide](https://github.com/hraness/ghostget/blob/v0.18.2/docs/control-panel.md).
|
|
@@ -17,34 +17,34 @@ If Bun is missing, stop and direct the user to the official
|
|
|
17
17
|
[Bun installation guide](https://bun.sh/docs/installation). Do not switch
|
|
18
18
|
package managers or pipe an unreviewed installer into a shell.
|
|
19
19
|
|
|
20
|
-
This reference is authored for the exact v0.18.
|
|
20
|
+
This reference is authored for the exact v0.18.2 release coordinate. Use it
|
|
21
21
|
only from the matching release-bound Agent Skill after its canonical archive and
|
|
22
22
|
immutable GitHub Release exist. If the coordinate is not public, stop instead
|
|
23
23
|
of substituting `main`, another tag, or a different package version. Install
|
|
24
24
|
that exact release and its reviewed bundled adapter manifests:
|
|
25
25
|
|
|
26
26
|
```sh
|
|
27
|
-
bun add --global https://github.com/hraness/ghostget/releases/download/v0.18.
|
|
27
|
+
bun add --global https://github.com/hraness/ghostget/releases/download/v0.18.2/hraness-ghostget-0.18.2.tgz
|
|
28
28
|
ghostget adapter sync-bundled --json
|
|
29
29
|
ghostget --help
|
|
30
30
|
ghostget doctor --json
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
The package is `@hraness/ghostget`; `@hraness/ghostget@0.18.
|
|
33
|
+
The package is `@hraness/ghostget`; `@hraness/ghostget@0.18.2` is an optional npm
|
|
34
34
|
mirror only after verified registry publication. Canonical installation does not
|
|
35
35
|
wait for registry publication.
|
|
36
36
|
|
|
37
37
|
When upgrading from Wrench, use `ghostget` for new commands and
|
|
38
38
|
`GHOSTGET_STATE_HOME` for an explicit state root. Existing state is selected in
|
|
39
39
|
place; do not rename, copy, or delete a state directory as part of the upgrade.
|
|
40
|
-
The [migration guide](https://github.com/hraness/ghostget/blob/v0.18.
|
|
40
|
+
The [migration guide](https://github.com/hraness/ghostget/blob/v0.18.2/docs/ghostget-migration.md)
|
|
41
41
|
explains the retained state aliases and durable protocol names.
|
|
42
42
|
|
|
43
43
|
Do not clone the repository merely to run the CLI. Importing the SDK is a
|
|
44
44
|
separate project dependency and does not install a global command:
|
|
45
45
|
|
|
46
46
|
```sh
|
|
47
|
-
bun add https://github.com/hraness/ghostget/releases/download/v0.18.
|
|
47
|
+
bun add https://github.com/hraness/ghostget/releases/download/v0.18.2/hraness-ghostget-0.18.2.tgz
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
`ghostget adapter sync-bundled` upgrades exact bundled baselines, including an
|
|
@@ -232,27 +232,55 @@ relationship on that vanity from the profile page. Current LinkedIn pages are
|
|
|
232
232
|
SDUI/RSC and often omit classic `bpr-guid-*` Profile embeds. The binder
|
|
233
233
|
therefore walks both those Voyager code payloads and `window.__como_rehydration__`.
|
|
234
234
|
Current pages assign that global as either a JSON object (`= { … }`) or an RSC
|
|
235
|
-
flight array (`= [ "1:I[…]\\n2:{…}" ]`).
|
|
236
|
-
flight
|
|
235
|
+
flight array (`= [ "1:I[…]\\n2:{…}" ]`). Some current arrays also carry a
|
|
236
|
+
non-flight SDUI string slot (not `1:…` rows) that still contains
|
|
237
|
+
`networkDistance` and `vieweeMemberUrn`. The binder accepts both assignment
|
|
238
|
+
shapes, decodes flight rows that carry JSON objects, arrays, or JSON strings,
|
|
239
|
+
feeds non-flight string slots through the same bootstrap decoder, and treats
|
|
237
240
|
`memberDistance`, `networkDistance`, or `distance` of `DISTANCE_1`,
|
|
238
241
|
`1`, or `"1"` as first-degree when that value is joined to the requested vanity
|
|
239
242
|
or its profile URN (`entityUrn`, `objectUrn`, `profileUrn`, `vieweeMemberUrn`,
|
|
240
243
|
or `vieweeProfileId` plus vanity). Current Como trees can nest around depth 60,
|
|
241
244
|
so the walk keeps a node ceiling and a depth ceiling of 128 instead of aborting
|
|
242
|
-
at depth 32. Distance may appear on PROFILE_VIEW breadcrumb or RSC string rows
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
at depth 32. Distance may appear on PROFILE_VIEW breadcrumb or RSC string rows,
|
|
246
|
+
including multi-escaped Como fragments such as `networkDistance\":1` beside a
|
|
247
|
+
`vieweeMemberUrn` or member id that is not the same `fsd_profile` URN bound
|
|
248
|
+
from `vieweeProfileId`. A unique first-degree distance on that viewee join is
|
|
249
|
+
accepted. Incidental viewer or other `fsd_profile` URNs
|
|
250
|
+
in the same breadcrumb do not steal identity. Empty, missing, ambiguous, or
|
|
251
|
+
contradictory distances stay fail-closed.
|
|
245
252
|
|
|
246
253
|
When the same page already embeds Contact-info fields, including a labeled
|
|
247
254
|
Email row, the operation projects those fields and does not issue a second
|
|
248
|
-
fetch.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
255
|
+
fetch. Long SDUI strings used as field labels are skipped instead of aborting
|
|
256
|
+
the profile-stage walk. Otherwise it first inspects the page's exact
|
|
257
|
+
Contact-info NavigateToScreen action. A reviewed action still admits only
|
|
258
|
+
the ProfileContactDetailsOverlay URL (`sduiid` equal to that overlay
|
|
259
|
+
`screenId`). Adapter 1.36.2 then clicks the unique accessible name
|
|
260
|
+
`Contact info` on the already-loaded 1st-degree profile and projects Email
|
|
261
|
+
from that modal DOM, including when that control's `href` matches
|
|
262
|
+
`/overlay/contact-info/` and contained Chrome follows that reviewed vanity
|
|
263
|
+
overlay pathname after click. Malformed or unsupported action fields stop
|
|
264
|
+
the operation. Zero or two-plus matching controls, a path other than the
|
|
265
|
+
bound profile or `/in/:publicIdentifier/overlay/contact-info/`, or a
|
|
266
|
+
missing or ambiguous dialog, fail closed. The contained path does not mint
|
|
267
|
+
a synthetic navigation POST or track headers, and it does not fetch that
|
|
268
|
+
overlay href as a navigation GET. Only when the
|
|
269
|
+
action is absent
|
|
270
|
+
does it use a page-resolved GraphQL `queryId` for
|
|
271
|
+
`voyagerIdentityDashProfileContactInfo`, when the HTML contains exactly one
|
|
272
|
+
decorated revision. Variables are exactly `(profileUrn:{urn})`. When that
|
|
273
|
+
queryId is absent, or when the GraphQL GET returns a reviewed HTTP 403
|
|
274
|
+
`text/html` rejection, the operation GETs the exact vanity
|
|
275
|
+
`/in/:publicIdentifier/overlay/contact-info/` overlay with RSC browser
|
|
276
|
+
binding. ScreenId-only and `profileUrn`-bound
|
|
277
|
+
`/flagship-web/rsc-action/actions/navigation` overlay GET URLs stay rejected
|
|
278
|
+
because live dormant sessions return HTTP 500 `application/octet-stream`.
|
|
279
|
+
Classic `/voyager/api/identity/profiles/{vanity}/profileContactInfo` now
|
|
280
|
+
returns HTTP 410 and is rejected. The executable contract is those reviewed
|
|
281
|
+
first-party reads, the reviewed Contact info click and modal snapshot, or
|
|
282
|
+
the page-embedded fields. It accepts no caller-selected RSC body, selector,
|
|
283
|
+
or script. This contact read does not message, connect, or InMail.
|
|
256
284
|
|
|
257
285
|
The projection returns `email` when LinkedIn shows it, plus any of the vanity
|
|
258
286
|
profile link, connected-since date, phone numbers, websites, and birthday.
|
|
@@ -287,6 +315,174 @@ the binder required a classic URN on the vanity record and required
|
|
|
287
315
|
`networkDistance` on a decoded object. Adapter 1.24.0 joins `vieweeProfileId`
|
|
288
316
|
plus vanity and reads breadcrumb or RSC string-row distance.
|
|
289
317
|
|
|
318
|
+
A 2026-09-11 signed-in capture of another 1st-degree profile still bound
|
|
319
|
+
`profiles.read` and still reached a unique `vieweeProfileId` profile URN after
|
|
320
|
+
the 1.24.0 walk. `contacts.read` then failed closed because relationship
|
|
321
|
+
distance sat on a PROFILE_VIEW breadcrumb next to `vieweeMemberUrn` / member
|
|
322
|
+
ids, not on a vanity or `fsd_profile` record the binder treated as the same
|
|
323
|
+
target. Adapter 1.25.0 joins that unique viewee distance, including when the
|
|
324
|
+
breadcrumb also carries an incidental viewer URN, and still fails closed for
|
|
325
|
+
self, non-first-degree, and contradictory distances.
|
|
326
|
+
|
|
327
|
+
A later 2026-09-11 signed-in capture of another 1st-degree profile still bound
|
|
328
|
+
`profiles.read` and still joined `vieweeProfileId` identity after the 1.25.0
|
|
329
|
+
walk. `contacts.read` then failed closed because PROFILE_VIEW breadcrumbs kept
|
|
330
|
+
`networkDistance` and `vieweeMemberUrn` in multi-escaped Como string rows
|
|
331
|
+
(`networkDistance\":1`), so the walk produced no distance records for the
|
|
332
|
+
viewee join. Adapter 1.26.0 peels those JSON string escapes and reads the
|
|
333
|
+
escaped key/value form. Self, non-first-degree, and contradictory distances
|
|
334
|
+
still fail closed.
|
|
335
|
+
|
|
336
|
+
A later 2026-09-11 signed-in capture of another 1st-degree profile still bound
|
|
337
|
+
`profiles.read` and still joined `vieweeProfileId` identity after the 1.26.0
|
|
338
|
+
walk. `contacts.read` then failed closed because distance lived in Como
|
|
339
|
+
rehydration array slot `[6]`, a ~25KB SDUI string with `networkDistance` and
|
|
340
|
+
`vieweeMemberUrn`. That slot was not an RSC flight, so
|
|
341
|
+
`decodeComoRehydrationValue` discarded it (`[]`) before the 1.26.0 peel.
|
|
342
|
+
`decodeStringBootstrap` already recovered the distance record from the same
|
|
343
|
+
string. Adapter 1.27.0 feeds non-flight Como string slots through that
|
|
344
|
+
decoder and still routes true RSC flights through `decodeRscFlightRecords`.
|
|
345
|
+
Self, non-first-degree, and contradictory distances still fail closed.
|
|
346
|
+
|
|
347
|
+
A later signed-in capture of a known 1st-degree profile still bound
|
|
348
|
+
identity, profile HTML, and distance, but Contact-info GraphQL returned
|
|
349
|
+
HTTP 403 `text/html` and the page still omitted
|
|
350
|
+
`voyagerIdentityDashProfileContactInfo.<32hex>`. The signed-in UI opens
|
|
351
|
+
Contact info through `ProfileContactDetailsOverlay`, not GraphQL.
|
|
352
|
+
Adapter 1.28.0 GETs that overlay with the bound `profileUrn` when queryId
|
|
353
|
+
is absent or after that reviewed GraphQL rejection, and projects Email
|
|
354
|
+
from the RSC or SDUI overlay payload. ScreenId-only overlay URLs stay
|
|
355
|
+
rejected. Self, non-first-degree, and contradictory distances still fail
|
|
356
|
+
closed.
|
|
357
|
+
|
|
358
|
+
A later signed-in capture of the same 1st-degree path still bound
|
|
359
|
+
identity and distance, but the 1.28.0 navigation GET returned HTTP 500
|
|
360
|
+
`application/octet-stream` for screenId-only, `profileUrn`-bound, and
|
|
361
|
+
html variants. `/in/:publicIdentifier/overlay/contact-info/` returned
|
|
362
|
+
HTML 200 for the signed-in Contact-info modal. Long SDUI strings in
|
|
363
|
+
profile HTML also aborted the best-effort embedded walk at profile
|
|
364
|
+
stage and misreported as relationship-binding contract-drift. Adapter
|
|
365
|
+
1.29.0 skips non-bounded field labels during that walk and GETs the
|
|
366
|
+
exact vanity overlay path with RSC headers instead of the 500
|
|
367
|
+
navigation route. GraphQL Contact-info remains a 403 `text/html` path
|
|
368
|
+
when a unique queryId is present. Self, non-first-degree, and
|
|
369
|
+
contradictory distances still fail closed.
|
|
370
|
+
|
|
371
|
+
A later 2026-09-11 smoke of the same 1st-degree path after adapter 1.29.0
|
|
372
|
+
still bound identity and distance, and the vanity overlay GET returned
|
|
373
|
+
HTML 200. That body was the signed-in profile HTML shell (~500KB+): no
|
|
374
|
+
projectable Email, no mailto, and no Contact-info JSON or RSC flight.
|
|
375
|
+
GraphQL Contact-info stayed HTTP 403 `text/html` with `queryId=null`.
|
|
376
|
+
The live Contact-info Email still appears only after the headed UI opens
|
|
377
|
+
the modal. Dormant contained Chrome stays GET-only and cannot mint that
|
|
378
|
+
client-filled payload. Adapter 1.30.0 treats an HTML-200 shell as
|
|
379
|
+
omitted Contact-info fields after 1st-degree binding, still projects
|
|
380
|
+
overlay HTML that carries Como Email, a unique mailto, or an RSC/SDUI
|
|
381
|
+
flight, and keeps `/flagship-web/rsc-action/actions/navigation` overlay
|
|
382
|
+
GETs rejected.
|
|
383
|
+
|
|
384
|
+
A later headed signed-in capture of the same 1st-degree Contact-info
|
|
385
|
+
click showed Email in the modal. The first network response that
|
|
386
|
+
contained the Email label was POST
|
|
387
|
+
`/flagship-web/rsc-action/actions/navigation?screenId=com.linkedin.sdui.flagshipnav.profile.ProfileContactDetailsOverlay&sduiid=com.linkedin.sdui.flagshipnav.profile.ProfileContactDetailsOverlay`
|
|
388
|
+
with `Accept: */*`, `Content-Type: application/json`, no RSC or Next
|
|
389
|
+
router headers, and JSON body keys `clientArguments` and `isModal`. The
|
|
390
|
+
response was HTTP 200 `application/octet-stream` RSC flight. Headed
|
|
391
|
+
capture bound `sduiid` to that same overlay `screenId` constant. Dormant
|
|
392
|
+
profile HTML NavigateToScreen for Contact info carries `pageKey`
|
|
393
|
+
`profile_view_base_contact_details` and `requestedArguments.payload`
|
|
394
|
+
(`vanityName`, `givenName`, `familyName`, `isVanityNameResolved`) but
|
|
395
|
+
omits `sduiid`. Adapter 1.31.0 POSTs the exact allowlisted URL after
|
|
396
|
+
1st-degree bind, peels reviewed payload keys, and copies `$type` /
|
|
397
|
+
`requestedStateKeys` / `requestMetadata` only when those reviewed fields
|
|
398
|
+
are already on the action. It does not invent a stolen `sduiid`, `$type`,
|
|
399
|
+
or `requestMetadata`. A different `sduiid` fails closed. GraphQL
|
|
400
|
+
Contact-info stays a 403 `text/html` fallback. Navigation overlay GETs
|
|
401
|
+
stay rejected. The vanity overlay GET remains HTML-shell honesty when
|
|
402
|
+
the profile page omits that NavigateToScreen action. Soft-labels stay.
|
|
403
|
+
Keep cookies, `li_at`, CSRF, `JSESSIONID`, live emails, and raw HARs out
|
|
404
|
+
of Git.
|
|
405
|
+
|
|
406
|
+
A later signed-in smoke after adapter 1.31.0 still bound 1st-degree
|
|
407
|
+
distance and selected the navigation POST family, but live
|
|
408
|
+
NavigateToScreen `requestedArguments` keeps `requestMetadata` a sibling
|
|
409
|
+
of `payload` and uses `$type` values `proto.sdui.actions.requests.RequestedArguments`
|
|
410
|
+
and `proto.sdui.common.RequestMetadata`. Treating `requestMetadata` as an
|
|
411
|
+
unreviewed extra key, or rejecting `proto.sdui.*` in favor of
|
|
412
|
+
`com.linkedin.*` only, aborted before POST. A payload-only body then
|
|
413
|
+
returned HTTP 500. Adapter 1.32.0 copies that reviewed sibling shape,
|
|
414
|
+
lifts headed nested `payload.requestMetadata` to the sibling placement
|
|
415
|
+
when the two agree, and admits `proto.sdui.*` types only on this overlay
|
|
416
|
+
POST body. Extra keys and unrelated proto prefixes still fail closed.
|
|
417
|
+
|
|
418
|
+
A later signed-in smoke after adapter 1.32.0 peeled that sibling
|
|
419
|
+
`proto.sdui` shape as `kind=action`, then the POST still returned HTTP
|
|
420
|
+
500 `text/html`. The body sent empty sibling `RequestMetadata{$type}`
|
|
421
|
+
only. Headed 200 Email capture `POST-navigation-body-shape-20260912.md`
|
|
422
|
+
nests `requestMetadata` under `payload` with `$type`, `states: []`,
|
|
423
|
+
overlay `screenId`, and `knownTemplates: []`, and keeps
|
|
424
|
+
`clientArguments.$type` plus `requestedStateKeys: []`. Adapter 1.33.0
|
|
425
|
+
emits that exact headed shape, including when dormant HTML omits
|
|
426
|
+
metadata, copies reviewed string lists when the page already has them,
|
|
427
|
+
and does not invent opaque state objects. Contained Chrome already
|
|
428
|
+
sends `Accept: */*`, `Content-Type: application/json`, and session
|
|
429
|
+
`csrf-token`; it does not mint `X-Li-*` track values. The query string
|
|
430
|
+
stays `sduiid=` matching overlay `screenId`. If a later 500 persists
|
|
431
|
+
after this exact body, the remaining gap is headed-only non-empty
|
|
432
|
+
`states` / `knownTemplates`, not sibling placement.
|
|
433
|
+
|
|
434
|
+
A later signed-in smoke after adapter 1.33.0 kept that exact headed
|
|
435
|
+
body and still returned HTTP 500 `text/html` from contained Chrome.
|
|
436
|
+
The POST ran from the signed-in origin fetch context, not the loaded
|
|
437
|
+
profile document, so it omitted page-instance headers the SPA sets.
|
|
438
|
+
Adapter 1.34.0 opens the exact reviewed profile URL, copies a unique
|
|
439
|
+
`d_flagship3_profile*` `x-li-page-instance` from observed same-origin
|
|
440
|
+
rsc-action or voyager requests or that document, and forwards optional
|
|
441
|
+
`x-li-track` (`mpName` `web` or `voyager-web`),
|
|
442
|
+
`x-li-application-version`, `x-li-application-instance`,
|
|
443
|
+
`x-li-anchor-page-key`, and `x-li-rsc-stream=true` only when the same
|
|
444
|
+
observation already has those values.
|
|
445
|
+
|
|
446
|
+
A later signed-in smoke after adapter 1.34.0 kept that exact headed
|
|
447
|
+
body and forwarded page-instance, track (`mpName=web`), application
|
|
448
|
+
version/instance, anchor page key, and `rscStream=true`, then still
|
|
449
|
+
returned HTTP 500 `text/html`. The same contained-browser
|
|
450
|
+
`/flagship-web/rsc-action/` observations on the bound profile already
|
|
451
|
+
carried `x-li-page-instance-tracking-id`, `x-li-pageforestid`,
|
|
452
|
+
`x-li-traceparent`, and `x-li-tracestate`; 1.34.0 left those names off.
|
|
453
|
+
Adapter 1.35.0 copies those original strings from the same selected
|
|
454
|
+
rsc-action observation under a reviewed allowlist, copies
|
|
455
|
+
`x-li-layout-tree` only when that observation has it, and does not mint
|
|
456
|
+
any of those values when they are absent. Missing or malformed observed
|
|
457
|
+
values fail closed before POST. Live query key stays `sduiid=` (headed
|
|
458
|
+
200 Email POST and public SDUI `server-request` / `pagination` routes);
|
|
459
|
+
`sduid=` notes are transcription typos. Missing or ambiguous
|
|
460
|
+
page-instance fails closed before POST. Headed successful body
|
|
461
|
+
Content-Length was 400; compact emit at 398 stays.
|
|
462
|
+
|
|
463
|
+
A later signed-in headed proof on 2026-09-12 opened tessbloch, clicked
|
|
464
|
+
Contact info, and showed Email in the modal DOM. The same contained
|
|
465
|
+
synthetic navigation POST still returned HTTP 500 `text/html` after the
|
|
466
|
+
exact headed body and the full observed X-Li set. Adapter 1.36.0 kept
|
|
467
|
+
the page-instance bind, then clicked only the unique reviewed Contact
|
|
468
|
+
info control and projected that modal. Operator smoke the same day
|
|
469
|
+
found that unique control and fail-closed before click because its
|
|
470
|
+
href matched `/overlay/contact-info/`. The raw eval error was exactly
|
|
471
|
+
`Contact-info control targeted the vanity overlay GET`. Adapter 1.36.1
|
|
472
|
+
clicks that unique control even when the href is the vanity overlay
|
|
473
|
+
GET. Operator smoke after that click fail-closed because contained
|
|
474
|
+
Chrome left `/in/tessbloch/` for
|
|
475
|
+
`/in/tessbloch/overlay/contact-info/`. Adapter 1.36.2 treats that
|
|
476
|
+
reviewed vanity overlay pathname as still bound after click, then
|
|
477
|
+
snapshots the unique dialog. Authwall and any other path still fail
|
|
478
|
+
closed. It does not mint a navigation POST or track headers. Cloud has
|
|
479
|
+
no signed-in LinkedIn session; do not treat this landing as live green.
|
|
480
|
+
|
|
481
|
+
```sh
|
|
482
|
+
printf '%s' '{"profile_url":"https://www.linkedin.com/in/tessbloch/"}' \
|
|
483
|
+
| ghostget invoke linkedin-web contacts.read --input - --auth linkedin-dormant-20260911 --json
|
|
484
|
+
```
|
|
485
|
+
|
|
290
486
|
Self profiles fail closed with guidance to use `profiles.read`. Second-degree,
|
|
291
487
|
third-degree, and out-of-network profiles fail closed because LinkedIn hid
|
|
292
488
|
Contact info from that viewer. The operation never invents a hidden email.
|
|
@@ -20,7 +20,7 @@ Leave the state `capture-required` when current evidence is absent, ambiguous, e
|
|
|
20
20
|
## LinkedIn
|
|
21
21
|
|
|
22
22
|
- Use `linkedin-web` with a browser-session/cookie realm for consumer Home feed, a member's recent-activity posts, inbox folders, conversations, and native article-editor surfaces. The separate official `linkedin` OAuth adapter covers approved post, comment, reply, repost, and reaction scopes; it does not supply the consumer Home feed, recent-activity pager, or inbox.
|
|
23
|
-
- Current bundle 1.
|
|
23
|
+
- Current bundle 1.36.2 observes `linkedin-web profiles.read@1`, `organizations.read@1`, `contacts.read@1`, `feeds.read@2` for `feed=profile-activity`, `articles.draft.save@7`, and image-only `posts.publish@3`. The two profile-stat reads use a path-backed contained Chrome realm, bind the current member before projecting one exact self profile or requested organization Page, and keep the optional private connection read sequential with the self-profile read. `contacts.read@1` uses the same contained Chrome path, requires a 1st-degree relationship on the requested vanity from classic Profile embeds or Como rehydration (JSON object, RSC flight array, non-flight Como string slots through `decodeStringBootstrap`, `vieweeProfileId` plus vanity, and breadcrumb, string-row, or `vieweeMemberUrn` distance, including multi-escaped PROFILE_VIEW rows and when that member id is not the bound `fsd_profile` URN), and projects Contact-info email plus any shown profile link, connected-since date, phones, websites, or birthday from page-embedded fields while skipping non-bounded SDUI field labels, or from the modal opened by clicking the unique reviewed Contact info control on that already-loaded 1st-degree profile after the same page-instance bind, including when that control's href matches `/overlay/contact-info/` and the document pathname stays on the bound profile or that reviewed vanity overlay path. A present NavigateToScreen action still admits only the reviewed ProfileContactDetailsOverlay URL (`sduiid` equal to that overlay `screenId`); the contained path does not mint a synthetic navigation POST or track headers. GraphQL Contact-info stays a 403 `text/html` fallback when a unique queryId is present and no navigation action is bound. The vanity `/overlay/contact-info/` RSC GET remains HTML-shell honesty when the profile omits that action. An HTML-200 profile shell that omits Email fails as omitted Contact-info fields after that 1st-degree bind; overlay, navigation-flight, or modal HTML that still carries Como Email, a unique mailto, or an RSC/SDUI flight still projects. ScreenId-only, `profileUrn`-bound, and GET `/flagship-web/rsc-action/actions/navigation` overlay URLs stay rejected because live dormant sessions return HTTP 500. The profile-activity page uses the same contained Chrome path, binds the signed-in viewer, resolves the live `voyagerFeedDashProfileUpdates` query, and projects one recent-activity page with engagement counts when present; `feed=home` remains capture-required. The fixed browser evaluation performs only bounded, exact-route first-party fetches, except the reviewed unique Contact info click and modal snapshot on `contacts.read`; it does not accept a caller-selected selector. A cookie-only standalone client is not a fallback because current live evidence shows LinkedIn invalidates an exported `li_at` outside its browser/device context. The Article contract binds the numeric member subject to the normalized Article-author profile, creates or replaces one exact private draft, keeps a supplied cover in the Article banner rather than the body, and can preserve the independently read existing banner during an exact replacement without another cover upload. It supports paragraphs/H1/H2/native blockquotes, native HTTPS links, and 1–20 bounded inline JPEG/PNG/WebP images with required alt text and optional captions, and verifies the exact unpublished editor-response readback. Its fixed cover and inline-image single-upload registrations, byte transfers, autosaves, and readbacks run inside contained Chrome without DOM automation. The separate post contract stages a real-size optional PNG through bounded ordered commands, admits one exact image transfer and post create, durably retains the accepted share target, and independently verifies it. `linkedin-web media.publish@1` separately reserves an MP4 route but remains capture-required; the official `linkedin posts.publish` OAuth contract already observes MP4.
|
|
24
24
|
- Every other LinkedIn web operation remains capture-required. Explicit inert reservations cover inbox folders, one bounded page of recommended connections, one connection invitation, Article reads/publication, and other comment/message/repost surfaces. Their retained candidates do not confer executable internal requests.
|
|
25
25
|
- Exclude presence, messaging badges, delivery acknowledgements, seen/read receipts, and notification badge traffic from every R1 contract.
|
|
26
26
|
- Bind the current viewer's person/member identity to the auth realm. `organizations.read` views a Page and does not confer Page-actor authority. For organization actions, additionally bind the administered organization actor selected by the plan.
|