@hraness/ghostget 0.18.2 → 0.18.4
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 +40 -0
- package/README.md +7 -7
- package/dist/apple-photos-client.js +1 -1
- package/dist/beeper-client.js +1 -1
- package/dist/{index-hfbygww8.js → index-chc1s7rp.js} +1 -1
- package/docs/control-panel.md +2 -2
- package/package.json +5 -4
- package/skills/ghostget/references/control-panel.md +1 -1
- package/skills/ghostget/references/install.md +5 -5
- package/skills/ghostget/references/linkedin-adapter.md +28 -22
- package/skills/ghostget/references/platform-patterns.md +1 -1
- package/src/assets/adapters/linkedin/wrench-web-adapter.json +1 -1
- package/src/beeper-client-types.ts +1 -1
- package/src/cli-intro.ts +5 -0
- package/src/cli.ts +2 -0
- package/src/providers/linkedin-contact-program.ts +14 -18
- package/src/providers/linkedin-web-contact.ts +49 -0
- package/src/providers/x-web-runtime.ts +2 -2
- package/src/providers/x-web.ts +2 -2
- package/src/usage.ts +1 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,46 @@ Historical entries retain their original delivery coordinates.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.18.4
|
|
11
|
+
|
|
12
|
+
- Show a compact ASCII ghost before interactive root help. Piped output,
|
|
13
|
+
machine-readable commands, version output, and command help stay unchanged.
|
|
14
|
+
- Refine the shared marketing surfaces and use the ghost identity consistently.
|
|
15
|
+
Provider operations, account authority, and immutable archive contracts remain
|
|
16
|
+
unchanged.
|
|
17
|
+
|
|
18
|
+
## 0.18.3
|
|
19
|
+
|
|
20
|
+
- Apply the shared Lantern material to the homepage: luminous grid glass behind
|
|
21
|
+
the hero, a blurred header, warm open questions, and opaque reading surfaces.
|
|
22
|
+
Preserve clean marks, ordinary documentation, and the inert native preview.
|
|
23
|
+
|
|
24
|
+
- Click the unique reviewed Contact info control when the page-bound
|
|
25
|
+
NavigateToScreen extract is `absent` after adapter 1.36.2. Operator
|
|
26
|
+
smoke on 2026-09-12 was green for Email on tessbloch through DIRECT
|
|
27
|
+
transport that clicked after an absent extract, using the reviewed
|
|
28
|
+
ProfileContactDetailsOverlay `sduiid` and headed clientArguments
|
|
29
|
+
(bound vanity, optional givenName/familyName from profile HTML,
|
|
30
|
+
`isVanityNameResolved`, nested requestMetadata
|
|
31
|
+
states/screenId/knownTemplates, `isModal` true). Official
|
|
32
|
+
`contacts.read` still fell through that absent extract to GraphQL
|
|
33
|
+
or the vanity overlay GET (broken 500/HTML-shell path). The
|
|
34
|
+
contained path now uses the same click and modal snapshot as a
|
|
35
|
+
present action. It still does not mint a navigation POST or track
|
|
36
|
+
headers. Zero or two-plus controls and a missing or ambiguous
|
|
37
|
+
modal still fail closed. GraphQL 403 and vanity HTML-shell
|
|
38
|
+
honesty stay unused once that click is selected. Soft-labels
|
|
39
|
+
stay. Self, non-first-degree, and contradictory distances still
|
|
40
|
+
fail closed, and no email is invented. Adapter bundle 1.36.3.
|
|
41
|
+
This is the seventeenth live Contact-info drift after the 1.36.2
|
|
42
|
+
overlay pathname bind. Cloud has no signed-in LinkedIn session;
|
|
43
|
+
do not treat this landing as live green. Operator smoke:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
printf '%s' '{"profile_url":"https://www.linkedin.com/in/tessbloch/"}' \
|
|
47
|
+
| ghostget invoke linkedin-web contacts.read --input - --auth linkedin-main --json
|
|
48
|
+
```
|
|
49
|
+
|
|
10
50
|
- Keep the unique reviewed Contact info click bound after adapter
|
|
11
51
|
1.36.1 when contained Chrome follows the live vanity overlay path.
|
|
12
52
|
Operator smoke on 2026-09-12 clicked that unique control, then
|
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Bring the model, planner, and tool loop you prefer. Ghostget supplies precise we
|
|
|
21
21
|
capabilities with local custody and explicit evidence. Its native macOS control
|
|
22
22
|
panel manages accounts, operation permissions, human approvals, editable OpenAPI
|
|
23
23
|
interfaces, and a searchable local web request log. See the
|
|
24
|
-
[control panel guide](docs/control-panel.md) and [native source build](https://github.com/hraness/ghostget/blob/v0.18.
|
|
24
|
+
[control panel guide](docs/control-panel.md) and [native source build](https://github.com/hraness/ghostget/blob/v0.18.4/desktop/README.md).
|
|
25
25
|
|
|
26
26
|
The separate public web gateway admits exact HTTPS retrieval URLs under human
|
|
27
27
|
domain and path rules. Use `ghostget web request <url>` with the app open, and
|
|
@@ -51,7 +51,7 @@ ghostget plugin list
|
|
|
51
51
|
|
|
52
52
|
## Built-in provider catalog
|
|
53
53
|
|
|
54
|
-
This v0.18.
|
|
54
|
+
This v0.18.4 source tree supports executable actions for 20 services: Beeper,
|
|
55
55
|
Bluesky, ClasificadosOnline, Facebook, Facebook Groups, Facebook Marketplace,
|
|
56
56
|
GitHub, Gmail, Hacker News, Instagram, iMessage, LinkedIn, Reddit, Substack,
|
|
57
57
|
Threads, TikTok, Twitch, WhatsApp, X, and YouTube.
|
|
@@ -116,7 +116,7 @@ owns the narrow capability boundary that can sit beneath them.
|
|
|
116
116
|
Wrench is now Ghostget. The package name is `@hraness/ghostget`, and the CLI
|
|
117
117
|
command is `ghostget`. Read the [migration guide](docs/ghostget-migration.md)
|
|
118
118
|
before updating an existing installation. After its optional npm mirror is
|
|
119
|
-
verified public, `@hraness/ghostget@0.18.
|
|
119
|
+
verified public, `@hraness/ghostget@0.18.4` is also available from the registry.
|
|
120
120
|
|
|
121
121
|
This README describes the package version in this source tree. Its versioned
|
|
122
122
|
GitHub archive and Agent Skill become a supported public release after the
|
|
@@ -127,9 +127,9 @@ The optional npm mirror can follow later without delaying canonical delivery.
|
|
|
127
127
|
Install the single Ghostget Agent Skill with either runner:
|
|
128
128
|
|
|
129
129
|
```sh
|
|
130
|
-
npx skills add hraness/ghostget#v0.18.
|
|
130
|
+
npx skills add hraness/ghostget#v0.18.4
|
|
131
131
|
# or
|
|
132
|
-
bunx skills add hraness/ghostget#v0.18.
|
|
132
|
+
bunx skills add hraness/ghostget#v0.18.4
|
|
133
133
|
```
|
|
134
134
|
|
|
135
135
|
The skill teaches Codex, Claude Code, Cursor, and other compatible coding
|
|
@@ -139,7 +139,7 @@ install the CLI if it is missing. Start a new agent session after installation.
|
|
|
139
139
|
After the matching immutable Release exists, install this exact canonical archive:
|
|
140
140
|
|
|
141
141
|
```sh
|
|
142
|
-
bun add --global https://github.com/hraness/ghostget/releases/download/v0.18.
|
|
142
|
+
bun add --global https://github.com/hraness/ghostget/releases/download/v0.18.4/hraness-ghostget-0.18.4.tgz
|
|
143
143
|
ghostget adapter sync-bundled --json
|
|
144
144
|
ghostget doctor
|
|
145
145
|
```
|
|
@@ -167,7 +167,7 @@ For that same released coordinate, install Ghostget in an agent or application
|
|
|
167
167
|
that owns its own model, planning, tool loop, approvals, and interface:
|
|
168
168
|
|
|
169
169
|
```sh
|
|
170
|
-
bun add https://github.com/hraness/ghostget/releases/download/v0.18.
|
|
170
|
+
bun add https://github.com/hraness/ghostget/releases/download/v0.18.4/hraness-ghostget-0.18.4.tgz
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
```ts
|
package/dist/beeper-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.4/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.4/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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hraness/ghostget",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.4",
|
|
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",
|
|
@@ -274,6 +274,7 @@
|
|
|
274
274
|
"src/client-types.ts",
|
|
275
275
|
"src/client.ts",
|
|
276
276
|
"src/cli.ts",
|
|
277
|
+
"src/cli-intro.ts",
|
|
277
278
|
"src/cursor-token.ts",
|
|
278
279
|
"src/derive-command-helper.ts",
|
|
279
280
|
"src/derivation-dnr-readiness.ts",
|
|
@@ -637,9 +638,9 @@
|
|
|
637
638
|
"@1password/sdk": "0.5.0"
|
|
638
639
|
},
|
|
639
640
|
"devDependencies": {
|
|
640
|
-
"@hraness/design-kit": "github:hraness/design-kit#v0.
|
|
641
|
-
"@hraness/site-footer": "github:hraness/site-footer#v0.6.
|
|
642
|
-
"@hraness/ui": "github:hraness/ui#v0.5.
|
|
641
|
+
"@hraness/design-kit": "github:hraness/design-kit#v0.8.0",
|
|
642
|
+
"@hraness/site-footer": "github:hraness/site-footer#v0.6.3",
|
|
643
|
+
"@hraness/ui": "github:hraness/ui#v0.5.13",
|
|
643
644
|
"@steipete/sweet-cookie": "0.4.3",
|
|
644
645
|
"@types/bun": "^1.3.14",
|
|
645
646
|
"@types/react": "19.2.14",
|
|
@@ -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.4/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.4 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.4/hraness-ghostget-0.18.4.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.4` 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.4/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.4/hraness-ghostget-0.18.4.tgz
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
`ghostget adapter sync-bundled` upgrades exact bundled baselines, including an
|
|
@@ -256,31 +256,31 @@ fetch. Long SDUI strings used as field labels are skipped instead of aborting
|
|
|
256
256
|
the profile-stage walk. Otherwise it first inspects the page's exact
|
|
257
257
|
Contact-info NavigateToScreen action. A reviewed action still admits only
|
|
258
258
|
the ProfileContactDetailsOverlay URL (`sduiid` equal to that overlay
|
|
259
|
-
`screenId`). Adapter 1.36.
|
|
259
|
+
`screenId`). Adapter 1.36.3 then clicks the unique accessible name
|
|
260
260
|
`Contact info` on the already-loaded 1st-degree profile and projects Email
|
|
261
261
|
from that modal DOM, including when that control's `href` matches
|
|
262
262
|
`/overlay/contact-info/` and contained Chrome follows that reviewed vanity
|
|
263
|
-
overlay pathname after click.
|
|
264
|
-
the
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
action
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
263
|
+
overlay pathname after click. When the NavigateToScreen extract is
|
|
264
|
+
`absent`, the same click still uses that reviewed overlay `sduiid` plus a
|
|
265
|
+
headed clientArguments payload from the bound vanity, optional
|
|
266
|
+
givenName/familyName parsed from profile HTML when unique,
|
|
267
|
+
`isVanityNameResolved`, nested requestMetadata
|
|
268
|
+
states/screenId/knownTemplates, and `isModal` true. Malformed or
|
|
269
|
+
unsupported action fields stop the operation. Zero or two-plus matching
|
|
270
|
+
controls, a path other than the bound profile or
|
|
271
|
+
`/in/:publicIdentifier/overlay/contact-info/`, or a missing or ambiguous
|
|
272
|
+
dialog, fail closed. The contained path does not mint a synthetic
|
|
273
|
+
navigation POST or track headers, and it does not fetch that overlay href
|
|
274
|
+
as a navigation GET. GraphQL `voyagerIdentityDashProfileContactInfo` and
|
|
275
|
+
the vanity overlay RSC GET stay unused once that click is selected.
|
|
276
|
+
ScreenId-only and `profileUrn`-bound
|
|
277
277
|
`/flagship-web/rsc-action/actions/navigation` overlay GET URLs stay rejected
|
|
278
278
|
because live dormant sessions return HTTP 500 `application/octet-stream`.
|
|
279
279
|
Classic `/voyager/api/identity/profiles/{vanity}/profileContactInfo` now
|
|
280
|
-
returns HTTP 410 and is rejected. The executable contract is
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
280
|
+
returns HTTP 410 and is rejected. The executable contract is the reviewed
|
|
281
|
+
Contact info click and modal snapshot, or the page-embedded fields. It
|
|
282
|
+
accepts no caller-selected RSC body, selector, or script. This contact
|
|
283
|
+
read does not message, connect, or InMail.
|
|
284
284
|
|
|
285
285
|
The projection returns `email` when LinkedIn shows it, plus any of the vanity
|
|
286
286
|
profile link, connected-since date, phone numbers, websites, and birthday.
|
|
@@ -474,9 +474,15 @@ GET. Operator smoke after that click fail-closed because contained
|
|
|
474
474
|
Chrome left `/in/tessbloch/` for
|
|
475
475
|
`/in/tessbloch/overlay/contact-info/`. Adapter 1.36.2 treats that
|
|
476
476
|
reviewed vanity overlay pathname as still bound after click, then
|
|
477
|
-
snapshots the unique dialog.
|
|
478
|
-
|
|
479
|
-
|
|
477
|
+
snapshots the unique dialog. Operator smoke after that bind was green
|
|
478
|
+
for Email on tessbloch when the extract was `absent` and the operator
|
|
479
|
+
clicked with the reviewed overlay `sduiid` plus headed clientArguments.
|
|
480
|
+
Official `contacts.read` still used GraphQL or the vanity overlay GET
|
|
481
|
+
on that absent extract. Adapter 1.36.3 uses the same click and modal
|
|
482
|
+
snapshot when extract is `absent`. Authwall and any other path still
|
|
483
|
+
fail closed. It does not mint a navigation POST or track headers.
|
|
484
|
+
Cloud has no signed-in LinkedIn session; do not treat this landing as
|
|
485
|
+
live green.
|
|
480
486
|
|
|
481
487
|
```sh
|
|
482
488
|
printf '%s' '{"profile_url":"https://www.linkedin.com/in/tessbloch/"}' \
|
|
@@ -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.36.
|
|
23
|
+
- Current bundle 1.36.3 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`). When that extract is `absent`, the same click still uses that reviewed overlay `sduiid` plus a headed clientArguments payload from the bound vanity, optional givenName/familyName from profile HTML, `isVanityNameResolved`, and nested requestMetadata. The contained path does not mint a synthetic navigation POST or track headers. GraphQL Contact-info and the vanity `/overlay/contact-info/` RSC GET stay unused once that click is selected. 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.
|
|
@@ -96,7 +96,7 @@ export type BeeperContactInteractionExportReceipt = Readonly<{
|
|
|
96
96
|
implementation: Readonly<{
|
|
97
97
|
producer: Readonly<{
|
|
98
98
|
package: "@hraness/ghostget";
|
|
99
|
-
version: "0.18.
|
|
99
|
+
version: "0.18.4";
|
|
100
100
|
}>;
|
|
101
101
|
officialCli: Readonly<{
|
|
102
102
|
implementation: "github.com/beeper/cli";
|
package/src/cli-intro.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Compact ASCII identity for interactive root help; never command output. */
|
|
2
|
+
export function terminalIntro(terminal: Readonly<{ isTTY: boolean | undefined; columns: number | undefined; term: string | undefined }>): string {
|
|
3
|
+
if (terminal.isTTY !== true || terminal.term === "dumb" || (terminal.columns ?? 80) < 48) return "";
|
|
4
|
+
return " .---.\n / o o \\ ghostget\n | | Get useful context for your agents.\n |/|/|/\n\n";
|
|
5
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { ghostgetUsage } from "./usage";
|
|
4
|
+
import { terminalIntro } from "./cli-intro";
|
|
4
5
|
import { GHOSTGET_VERSION } from "./version";
|
|
5
6
|
import type {
|
|
6
7
|
GhostgetCatalogCommand,
|
|
@@ -164,6 +165,7 @@ export async function runGhostgetCliProcess(
|
|
|
164
165
|
loadKnowledgeCli: () => Promise<PublicKbCliModule> = loadPublicKbCli,
|
|
165
166
|
): Promise<void> {
|
|
166
167
|
if (isImmediateGhostgetHelpRequest(rawArguments)) {
|
|
168
|
+
if (output === defaultOutput) output.stdout(terminalIntro({ isTTY: process.stdout.isTTY, columns: process.stdout.columns, term: process.env.TERM }));
|
|
167
169
|
output.stdout(ghostgetUsage);
|
|
168
170
|
process.exitCode = 0;
|
|
169
171
|
return;
|
|
@@ -4,18 +4,17 @@ import { readAttempt, type ReadEffectFailure, withReadResource } from "../read-e
|
|
|
4
4
|
import type { WebSessionExecution } from "../web-session-execution";
|
|
5
5
|
import { LinkedInContactPlatform } from "./linkedin-contact-platform";
|
|
6
6
|
import {
|
|
7
|
-
isLinkedInContactGraphqlUnavailable,
|
|
8
7
|
linkedInContactDiagnostic,
|
|
9
8
|
linkedInContactReadFailure,
|
|
10
9
|
type LinkedInContactStage,
|
|
11
10
|
} from "./linkedin-contact-failure";
|
|
12
11
|
import {
|
|
12
|
+
buildLinkedInAbsentContactNavigationAction,
|
|
13
13
|
extractLinkedInContactNavigationAction,
|
|
14
14
|
linkedInContactNavigationActionError,
|
|
15
15
|
projectLinkedInContactInfo,
|
|
16
16
|
projectLinkedInEmbeddedContactFields,
|
|
17
17
|
projectLinkedInProfileContactBinding,
|
|
18
|
-
resolveLinkedInProfileContactInfoQueryId,
|
|
19
18
|
type LinkedInContactInfoTarget,
|
|
20
19
|
} from "./linkedin-web-contact";
|
|
21
20
|
|
|
@@ -65,25 +64,22 @@ export function linkedInContactReadProgram(
|
|
|
65
64
|
sduiid: navigation.action.sduiid,
|
|
66
65
|
clientArguments: navigation.action.clientArguments,
|
|
67
66
|
});
|
|
68
|
-
} else if (navigation.kind
|
|
67
|
+
} else if (navigation.kind === "absent") {
|
|
68
|
+
const fallback = yield* readAttempt(() => buildLinkedInAbsentContactNavigationAction({
|
|
69
|
+
profileHtml,
|
|
70
|
+
publicIdentifier: binding.vanity,
|
|
71
|
+
profileUrn: binding.profileUrn,
|
|
72
|
+
}));
|
|
73
|
+
contactPayload = yield* platform.contactNavigation(browser, {
|
|
74
|
+
profileUrl: target.url,
|
|
75
|
+
profileUrn: binding.profileUrn,
|
|
76
|
+
sduiid: fallback.sduiid,
|
|
77
|
+
clientArguments: fallback.clientArguments,
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
69
80
|
yield* readAttempt(() => {
|
|
70
81
|
throw linkedInContactNavigationActionError(navigation);
|
|
71
82
|
});
|
|
72
|
-
} else {
|
|
73
|
-
const queryId = resolveLinkedInProfileContactInfoQueryId(profileHtml);
|
|
74
|
-
const contactInput = {
|
|
75
|
-
profileUrl: target.url,
|
|
76
|
-
profileUrn: binding.profileUrn,
|
|
77
|
-
...(queryId === undefined ? {} : { queryId }),
|
|
78
|
-
};
|
|
79
|
-
contactPayload = queryId === undefined
|
|
80
|
-
? yield* platform.contactOverlay(browser, contactInput)
|
|
81
|
-
: yield* platform.contactPayload(browser, contactInput).pipe(
|
|
82
|
-
Effect.catchIf(
|
|
83
|
-
(error) => isLinkedInContactGraphqlUnavailable(error),
|
|
84
|
-
() => platform.contactOverlay(browser, contactInput),
|
|
85
|
-
),
|
|
86
|
-
);
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
85
|
stage = "projection";
|
|
@@ -1571,6 +1571,55 @@ export function linkedInContactNavigationActionError(
|
|
|
1571
1571
|
);
|
|
1572
1572
|
}
|
|
1573
1573
|
|
|
1574
|
+
export function extractLinkedInProfileContactNames(input: {
|
|
1575
|
+
readonly profileHtml: unknown;
|
|
1576
|
+
readonly publicIdentifier: string;
|
|
1577
|
+
}): {
|
|
1578
|
+
readonly givenName?: string;
|
|
1579
|
+
readonly familyName?: string;
|
|
1580
|
+
} {
|
|
1581
|
+
const publicIdentifier = linkedInPersonalProfilePublicIdentifier(input.publicIdentifier);
|
|
1582
|
+
const given = new Set<string>();
|
|
1583
|
+
const family = new Set<string>();
|
|
1584
|
+
for (const record of embeddedRecords(input.profileHtml)) {
|
|
1585
|
+
if (vanityFromRecord(record) !== publicIdentifier) continue;
|
|
1586
|
+
const givenName = optionalPersonName(record.givenName ?? record.firstName);
|
|
1587
|
+
const familyName = optionalPersonName(record.familyName ?? record.lastName);
|
|
1588
|
+
if (givenName !== null) given.add(givenName);
|
|
1589
|
+
if (familyName !== null) family.add(familyName);
|
|
1590
|
+
}
|
|
1591
|
+
const uniqueGiven = given.size === 1 ? [...given][0] : undefined;
|
|
1592
|
+
const uniqueFamily = family.size === 1 ? [...family][0] : undefined;
|
|
1593
|
+
return Object.freeze({
|
|
1594
|
+
...(uniqueGiven === undefined ? {} : { givenName: uniqueGiven }),
|
|
1595
|
+
...(uniqueFamily === undefined ? {} : { familyName: uniqueFamily }),
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
export function buildLinkedInAbsentContactNavigationAction(input: {
|
|
1600
|
+
readonly profileHtml: unknown;
|
|
1601
|
+
readonly publicIdentifier: string;
|
|
1602
|
+
readonly profileUrn: string;
|
|
1603
|
+
}): LinkedInContactNavigationAction {
|
|
1604
|
+
const publicIdentifier = linkedInPersonalProfilePublicIdentifier(input.publicIdentifier);
|
|
1605
|
+
profileUrn(input.profileUrn);
|
|
1606
|
+
const names = extractLinkedInProfileContactNames({
|
|
1607
|
+
profileHtml: input.profileHtml,
|
|
1608
|
+
publicIdentifier,
|
|
1609
|
+
});
|
|
1610
|
+
return Object.freeze({
|
|
1611
|
+
sduiid: LINKEDIN_CONTACT_DETAILS_OVERLAY_SCREEN_ID,
|
|
1612
|
+
clientArguments: canonicalContactNavigationClientArguments({
|
|
1613
|
+
payload: {
|
|
1614
|
+
vanityName: publicIdentifier,
|
|
1615
|
+
...names,
|
|
1616
|
+
isVanityNameResolved: true,
|
|
1617
|
+
},
|
|
1618
|
+
}),
|
|
1619
|
+
isModal: true,
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1574
1623
|
export function extractLinkedInContactNavigationAction(input: {
|
|
1575
1624
|
readonly profileHtml: unknown;
|
|
1576
1625
|
readonly publicIdentifier: string;
|
|
@@ -153,8 +153,8 @@ const viewerEvidence = Object.freeze({
|
|
|
153
153
|
operationName: "Viewer",
|
|
154
154
|
operationType: "query" as const,
|
|
155
155
|
queryId: "9t128XgFic52jPUEkJMf6w",
|
|
156
|
-
sourceChunk: "main.
|
|
157
|
-
observedOn: "2026-09-
|
|
156
|
+
sourceChunk: "main.59435dbf6f40166da.js",
|
|
157
|
+
observedOn: "2026-09-13",
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
function isRecord(value: unknown): value is JsonRecord {
|
package/src/providers/x-web.ts
CHANGED
|
@@ -246,7 +246,7 @@ export const xWebQueryDescriptorEvidenceSnapshot = Object.freeze({
|
|
|
246
246
|
{ operationName: "HomeLatestTimeline", operationType: "query", queryId: "BLQWpfVqtgBqAqwRRJcJjA", sourceChunk: "shared~bundle.LoggedInMain~bundle.HomeTimeline.c12c8a9a.js", observedOn: "2026-08-20" },
|
|
247
247
|
{ operationName: "ListLatestTweetsTimeline", operationType: "query", queryId: "LV64djPRhnsVhGCK76s13w", sourceChunk: "shared~loader.Dock~bundle.BookmarkFolders~bundle.Bookmarks~bundle.Explore~bundle.HomeTimeline~bundle.Notifica.3b894e0a.js" },
|
|
248
248
|
{ operationName: "ListRankedTweetsTimeline", operationType: "query", queryId: "dPN7GrkxeMF4SUYCo9D9YA", sourceChunk: "shared~loader.Dock~bundle.BookmarkFolders~bundle.Bookmarks~bundle.Explore~bundle.HomeTimeline~bundle.Notifica.3b894e0a.js" },
|
|
249
|
-
{ operationName: "Bookmarks", operationType: "query", queryId: "tF6KOjmZM0WGcB2Q0mfwhw", sourceChunk: "shared~bundle.BookmarkFolders~bundle.Bookmarks.
|
|
249
|
+
{ operationName: "Bookmarks", operationType: "query", queryId: "tF6KOjmZM0WGcB2Q0mfwhw", sourceChunk: "shared~bundle.BookmarkFolders~bundle.Bookmarks.42881d01aa4e5708a.js", observedOn: "2026-09-13" },
|
|
250
250
|
{ operationName: "BookmarkSearchTimeline", operationType: "query", queryId: "SpDsqmz6FfYESd1e7TPcAw", sourceChunk: "main.9929b02a.js" },
|
|
251
251
|
{ operationName: "SearchTimeline", operationType: "query", queryId: "hyPfJYJ_XAtDYoslQc-Rgg", sourceChunk: "main.ae82e9d02d3328bba.js", observedOn: "2026-09-06" },
|
|
252
252
|
{ operationName: "NotificationsTimeline", operationType: "query", queryId: "dDSNxYH-uWwVo2r3Y5VVqg", sourceChunk: "bundle.Notifications.eea6257a.js" },
|
|
@@ -268,7 +268,7 @@ export const xWebQueryDescriptorEvidenceSnapshot = Object.freeze({
|
|
|
268
268
|
{ operationName: "DmAllSearchSlice", operationType: "query", queryId: "zd0F6a_svKAXdlMGbCZDFg", sourceChunk: "bundle.DirectMessages.265735ba.js" },
|
|
269
269
|
{ operationName: "DmGroupSearchSlice", operationType: "query", queryId: "LxrvmqF3Lokl_BYZ1c83LA", sourceChunk: "bundle.DirectMessages.265735ba.js" },
|
|
270
270
|
{ operationName: "DmPeopleSearchSlice", operationType: "query", queryId: "c1MnRRmI-_Bggpntlq9-hQ", sourceChunk: "bundle.DirectMessages.265735ba.js" },
|
|
271
|
-
{ operationName: "Viewer", operationType: "query", queryId: "9t128XgFic52jPUEkJMf6w", sourceChunk: "main.
|
|
271
|
+
{ operationName: "Viewer", operationType: "query", queryId: "9t128XgFic52jPUEkJMf6w", sourceChunk: "main.59435dbf6f40166da.js", observedOn: "2026-09-13" },
|
|
272
272
|
{ operationName: "UserByScreenName", operationType: "query", queryId: "Gb-d6r0vxPOADdG62OEBpQ", sourceChunk: "main.dd6a5b6a.js", observedOn: "2026-08-21" },
|
|
273
273
|
{ operationName: "ArticleEntityDraftCreate", operationType: "mutation", queryId: "btD9FyMDa3_vydVp7fr87Q", sourceChunk: "bundle.TwitterArticles.305538ca.js", observedOn: "2026-08-14" },
|
|
274
274
|
{ operationName: "ArticleEntityUpdateContent", operationType: "mutation", queryId: "P5Nc3DYs9D4XqVthNrig8w", sourceChunk: "bundle.TwitterArticles.305538ca.js", observedOn: "2026-08-14" },
|
package/src/usage.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static CLI help kept separate from the command implementation graph.
|
|
3
3
|
*
|
|
4
|
-
* The installed entrypoint imports only
|
|
4
|
+
* The installed entrypoint imports only static help, terminal identity, and release
|
|
5
5
|
* identity for a valid help request, so help remains available even when an
|
|
6
6
|
* optional provider runtime is broken.
|
|
7
7
|
*/
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Canonical immutable Ghostget package release identity. */
|
|
2
|
-
export const GHOSTGET_VERSION = "0.18.
|
|
2
|
+
export const GHOSTGET_VERSION = "0.18.4" as const;
|