@staff0rd/assist 0.306.0 → 0.307.0
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/README.md
CHANGED
|
@@ -224,7 +224,8 @@ The first backlog command in a repository that still has a local `.assist/backlo
|
|
|
224
224
|
- `assist sql mutate "<sql>" [connection]` - Execute a mutating SQL statement and print rows affected (rejects non-mutating statements like pure SELECTs)
|
|
225
225
|
- `assist sql tables [connection]` - List tables in the connected database (via INFORMATION_SCHEMA.TABLES)
|
|
226
226
|
- `assist sql columns <table> [connection]` - List columns for a table (use `schema.table` for non-default schema; via INFORMATION_SCHEMA.COLUMNS)
|
|
227
|
-
- `assist netcap [-p, --port <port>]` - Start a local receiver that captures browser network traffic to a JSONL file (default `~/.assist/netcap
|
|
227
|
+
- `assist netcap [-p, --port <port>] [-o, --out <dir>] [-f, --filter <pattern>]` - Start a local receiver that captures browser network traffic to a JSONL file (`capture.jsonl` under `--out`, default `~/.assist/netcap`), paired with the netcap browser extension. Sends permissive CORS, logs each ping/capture live, and runs until Ctrl-C, then prints a capture count (default port `8723`). `--filter` bakes a URL substring into the extension so only matching requests forward. See [netcap browser extension](#netcap-browser-extension)
|
|
228
|
+
- `assist netcap extract [file]` - Parse a netcap capture file into structured posts (`text`, `markdown` with mentions linked, `author`, `mentions`, `hashtags`, `links`, `relatedPosts`, and `postedAt` decoded from the activity id) and write them to `posts.json` beside the capture (defaults to `~/.assist/netcap/capture.jsonl`). Reads both the LinkedIn SDUI (rsc-action) responses rendered on first paint and the voyager GraphQL profile-updates responses loaded on scroll, deduped by activity urn keeping the richest copy of each post
|
|
228
229
|
- `assist screenshot <process>` - Capture a screenshot of a running application window (e.g. `assist screenshot notepad`). Output directory is configurable via `screenshot.outputDir` (default `./screenshots`)
|
|
229
230
|
- `assist handover save --summary <s>` - Save a session handover note to the backlog DB (content read from stdin), scoped by the repo's git origin. Appends a new row each call; `--summary` is the one-line description shown when recalling
|
|
230
231
|
- `assist handover list` - List unrecalled handovers for this repo, most recent first, one per line as tab-separated `id`, ISO-8601 created timestamp, and one-line summary. Prints nothing when none are pending
|
|
@@ -277,11 +278,13 @@ When `commit.pull` is enabled in config, `assist draft`, `assist bug`, `assist r
|
|
|
277
278
|
|
|
278
279
|
## netcap browser extension
|
|
279
280
|
|
|
280
|
-
`assist netcap` only runs the receiver; the browser side is a raw Manifest V3 extension (no build step) under `netcap-extension/`. A MAIN-world content script patches `fetch`/`XMLHttpRequest` to capture `{url, method, status, requestBody, responseBody, timestamp}`, relays each entry to the background service worker (`window.postMessage` → `chrome.runtime.sendMessage`), and the background worker POSTs it to the receiver. Forwarding happens in the background context, so the page's CSP (`connect-src`) never blocks it — the limitation that killed the earlier console-paste approach.
|
|
281
|
+
`assist netcap` only runs the receiver; the browser side is a raw Manifest V3 extension (no build step) under `netcap-extension/`. A MAIN-world content script patches `fetch`/`XMLHttpRequest` to capture `{url, method, status, requestBody, responseBody, timestamp}`, relays each entry to the background service worker (`window.postMessage` → `chrome.runtime.sendMessage`), and the background worker POSTs it to the receiver. The XHR patch reads the response across every `responseType` (`json`, `blob`, `arraybuffer`, `document`, not just `text`) — LinkedIn's voyager GraphQL calls use `responseType: "json"`, which exposes no `responseText`, so reading `responseText` alone captured those bodies empty. Forwarding happens in the background context, so the page's CSP (`connect-src`) never blocks it — the limitation that killed the earlier console-paste approach.
|
|
281
282
|
|
|
282
283
|
1. Run `assist netcap` — it prints the receiver URL, the capture file path, and the absolute path to the extension directory to load. The receiver host/port is baked into the extension's `background.js` at this point. Under WSL (where the browser runs on the Windows host, cannot load from a WSL path, and cannot reach the receiver on `localhost` because WSL2 localhost forwarding is unreliable) it copies the extension to `C:\tools\netcap-extension`, targets the WSL VM's IP, and prints that Windows path instead. Re-run `assist netcap` after a reboot (the WSL IP can change) and reload the extension.
|
|
283
284
|
2. Load the unpacked extension:
|
|
284
285
|
- **Firefox**: open `about:debugging#/runtime/this-firefox` → **Load Temporary Add-on…** → pick `manifest.json` inside the printed extension directory. (Requires Firefox 128+ for MAIN-world content scripts.)
|
|
285
286
|
- **Chrome**: open `chrome://extensions`, enable **Developer mode**, click **Load unpacked**, and select the extension directory.
|
|
286
287
|
3. On load the background worker pings the receiver; `ping from extension` appears in the `assist netcap` log, confirming browser→server connectivity.
|
|
287
|
-
4. Browse a site (e.g. LinkedIn activity); matching requests append to the capture file live and survive page refreshes
|
|
288
|
+
4. Browse a site (e.g. LinkedIn activity); matching requests append to the capture file live and survive page refreshes (re-loading the extension appends to the same file). Press Ctrl-C to stop the receiver; it prints how many entries were captured.
|
|
289
|
+
|
|
290
|
+
`assist netcap` bakes the receiver host/port and the optional `--filter` substring into the extension's `background.js` at startup, so you don't normally edit it by hand. With `--filter <pattern>`, the background worker only forwards requests whose URL contains `<pattern>` (case-sensitive substring); the matching logic mirrors `matchesNetcapFilter` in `src/commands/netcap`. Use `--out <dir>` to write `capture.jsonl` into a different directory.
|
|
@@ -3,17 +3,25 @@
|
|
|
3
3
|
// startup it pings the receiver (the connectivity spike) and thereafter
|
|
4
4
|
// forwards every captured entry relayed from the content scripts.
|
|
5
5
|
const RECEIVER = "http://127.0.0.1:8723/";
|
|
6
|
+
const FILTER = "";
|
|
6
7
|
|
|
7
8
|
function ping() {
|
|
8
9
|
fetch(`${RECEIVER}ping`, { method: "GET" }).catch(() => {});
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
// why: duplicates matchesNetcapFilter (src/commands/netcap) — plain JS, no TS import
|
|
13
|
+
function matchesFilter(url) {
|
|
14
|
+
if (!FILTER) return true;
|
|
15
|
+
return typeof url === "string" && url.includes(FILTER);
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
ping();
|
|
12
19
|
chrome.runtime.onInstalled?.addListener(ping);
|
|
13
20
|
chrome.runtime.onStartup?.addListener(ping);
|
|
14
21
|
|
|
15
22
|
chrome.runtime.onMessage.addListener((message) => {
|
|
16
23
|
if (!message || message.type !== "netcap-entry") return;
|
|
24
|
+
if (!matchesFilter(message.entry && message.entry.url)) return;
|
|
17
25
|
fetch(RECEIVER, {
|
|
18
26
|
method: "POST",
|
|
19
27
|
headers: { "Content-Type": "application/json" },
|
|
@@ -57,6 +57,24 @@
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// why: voyager GraphQL uses responseType "json"/"blob" (no responseText), so reading responseText alone captured them empty
|
|
61
|
+
function xhrResponseBody(xhr) {
|
|
62
|
+
try {
|
|
63
|
+
const type = xhr.responseType;
|
|
64
|
+
if (type === "" || type === "text") return xhr.responseText ?? "";
|
|
65
|
+
if (type === "json")
|
|
66
|
+
return xhr.response == null ? "" : JSON.stringify(xhr.response);
|
|
67
|
+
if (type === "arraybuffer")
|
|
68
|
+
return new TextDecoder().decode(xhr.response);
|
|
69
|
+
if (type === "document")
|
|
70
|
+
return xhr.response?.documentElement?.outerHTML ?? "";
|
|
71
|
+
if (type === "blob") return xhr.response;
|
|
72
|
+
} catch {
|
|
73
|
+
// ignore: response unavailable for this responseType
|
|
74
|
+
}
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
|
|
60
78
|
const OriginalXHR = window.XMLHttpRequest;
|
|
61
79
|
if (OriginalXHR) {
|
|
62
80
|
const open = OriginalXHR.prototype.open;
|
|
@@ -70,22 +88,24 @@
|
|
|
70
88
|
if (meta) {
|
|
71
89
|
meta.requestBody = bodyToString(body);
|
|
72
90
|
this.addEventListener("loadend", () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
const emit = (responseBody) =>
|
|
92
|
+
post({
|
|
93
|
+
url: meta.url,
|
|
94
|
+
method: meta.method,
|
|
95
|
+
status: this.status,
|
|
96
|
+
requestBody: meta.requestBody ?? null,
|
|
97
|
+
responseBody,
|
|
98
|
+
timestamp: Date.now(),
|
|
99
|
+
});
|
|
100
|
+
const value = xhrResponseBody(this);
|
|
101
|
+
if (value instanceof Blob) {
|
|
102
|
+
value
|
|
103
|
+
.text()
|
|
104
|
+
.then(emit)
|
|
105
|
+
.catch(() => emit(""));
|
|
106
|
+
} else {
|
|
107
|
+
emit(value);
|
|
80
108
|
}
|
|
81
|
-
post({
|
|
82
|
-
url: meta.url,
|
|
83
|
-
method: meta.method,
|
|
84
|
-
status: this.status,
|
|
85
|
-
requestBody: meta.requestBody ?? null,
|
|
86
|
-
responseBody,
|
|
87
|
-
timestamp: Date.now(),
|
|
88
|
-
});
|
|
89
109
|
});
|
|
90
110
|
}
|
|
91
111
|
return send.call(this, body);
|