cngkit 1.1.18 → 1.1.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/LICENSE +1 -1
- package/README.md +94 -19
- package/dist/chunk-CIZBVLN5.js +35 -0
- package/dist/chunk-CIZBVLN5.js.map +1 -0
- package/dist/{chunk-IB5B3BLY.js → chunk-E2GLGGKO.js} +16 -4
- package/dist/chunk-E2GLGGKO.js.map +1 -0
- package/dist/chunk-MRXGD6TC.js +42 -0
- package/dist/chunk-MRXGD6TC.js.map +1 -0
- package/dist/{chunk-ZA4YOWPB.js → chunk-NGEWD4BW.js} +2 -1
- package/dist/chunk-NODJM6SH.js +658 -0
- package/dist/chunk-NODJM6SH.js.map +1 -0
- package/dist/chunk-SKK2XLRZ.js +1590 -0
- package/dist/chunk-SKK2XLRZ.js.map +1 -0
- package/dist/chunk-SMTQ3W3F.js +271 -0
- package/dist/chunk-SMTQ3W3F.js.map +1 -0
- package/dist/{chunk-FJ34NVQ4.js → chunk-WDI43VPW.js} +578 -88
- package/dist/chunk-WDI43VPW.js.map +1 -0
- package/dist/cli.js +107 -27
- package/dist/cli.js.map +1 -1
- package/dist/commands/coderoom/index.js +6 -6
- package/dist/commands/coderoom/index.js.map +1 -1
- package/dist/commands/coderoom/join.js +5 -5
- package/dist/commands/coderoom/share.js +5 -5
- package/dist/commands/hookify/index.js +6 -6
- package/dist/commands/hookify/index.js.map +1 -1
- package/dist/commands/hookify/ingest.js +52 -13
- package/dist/commands/hookify/ingest.js.map +1 -1
- package/dist/commands/hooks/index.js +25 -0
- package/dist/commands/hooks/index.js.map +1 -0
- package/dist/commands/hooks/install.js +40 -0
- package/dist/commands/hooks/install.js.map +1 -0
- package/dist/commands/hooks/uninstall.js +40 -0
- package/dist/commands/hooks/uninstall.js.map +1 -0
- package/dist/commands/index.js +5 -5
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/knowledges/audiences.js +6 -6
- package/dist/commands/knowledges/cat.js +31 -0
- package/dist/commands/knowledges/cat.js.map +1 -0
- package/dist/commands/knowledges/files.js +6 -6
- package/dist/commands/knowledges/find.js +66 -0
- package/dist/commands/knowledges/find.js.map +1 -0
- package/dist/commands/knowledges/glob.js +6 -6
- package/dist/commands/knowledges/grep.js +6 -6
- package/dist/commands/knowledges/head.js +41 -0
- package/dist/commands/knowledges/head.js.map +1 -0
- package/dist/commands/knowledges/index.js +6 -6
- package/dist/commands/knowledges/index.js.map +1 -1
- package/dist/commands/knowledges/list.js +7 -7
- package/dist/commands/knowledges/list.js.map +1 -1
- package/dist/commands/knowledges/ls.js +16 -7
- package/dist/commands/knowledges/ls.js.map +1 -1
- package/dist/commands/knowledges/read.js +6 -6
- package/dist/commands/knowledges/realpath.js +31 -0
- package/dist/commands/knowledges/realpath.js.map +1 -0
- package/dist/commands/knowledges/search.js +6 -6
- package/dist/commands/knowledges/stat.js +31 -0
- package/dist/commands/knowledges/stat.js.map +1 -0
- package/dist/commands/knowledges/status.js +6 -6
- package/dist/commands/knowledges/tail.js +41 -0
- package/dist/commands/knowledges/tail.js.map +1 -0
- package/dist/commands/knowledges/tree.js +46 -0
- package/dist/commands/knowledges/tree.js.map +1 -0
- package/dist/commands/login.js +4 -4
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/scrub.js +38 -15
- package/dist/commands/scrub.js.map +1 -1
- package/dist/commands/transcripts.js +44 -24
- package/dist/commands/transcripts.js.map +1 -1
- package/package.json +3 -4
- package/dist/chunk-C7HFDK4S.js +0 -393
- package/dist/chunk-C7HFDK4S.js.map +0 -1
- package/dist/chunk-CBIVTEZP.js +0 -222
- package/dist/chunk-CBIVTEZP.js.map +0 -1
- package/dist/chunk-FJ34NVQ4.js.map +0 -1
- package/dist/chunk-IB5B3BLY.js.map +0 -1
- package/dist/chunk-KSW6QT5Q.js +0 -628
- package/dist/chunk-KSW6QT5Q.js.map +0 -1
- package/dist/chunk-TWQDLZ6F.js +0 -26
- package/dist/chunk-TWQDLZ6F.js.map +0 -1
- /package/dist/{chunk-ZA4YOWPB.js.map → chunk-NGEWD4BW.js.map} +0 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/cngkit)
|
|
4
4
|
[](https://nodejs.org/)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
CNG operator CLI for shared code rooms, safe local cleanup, local agent transcript
|
|
7
7
|
inspection, and terminal access to the hosted Harness knowledge catalog.
|
|
8
8
|
|
|
9
9
|
`cngkit` is built for developers and AI agents who need practical commands that work
|
|
@@ -42,22 +42,24 @@ cngkit coderoom join <room-code>
|
|
|
42
42
|
cngkit scrub [path]
|
|
43
43
|
cngkit transcripts list --limit 12
|
|
44
44
|
cngkit knowledges search Cloudflare --limit 3
|
|
45
|
-
cngkit knowledges
|
|
45
|
+
cngkit knowledges cat /libraries/lib-cloudflare/TOPIC.md
|
|
46
|
+
cngkit hooks install
|
|
46
47
|
printf '{"hook":"PreToolUse"}' | cngkit hookify ingest --event PreToolUse
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
The CLI currently has
|
|
50
|
+
The CLI currently has six main jobs:
|
|
50
51
|
|
|
51
52
|
- **Coderoom**: start or join a live shared working-tree room.
|
|
52
53
|
- **Scrub**: scan local files for secrets and optionally mask them inline.
|
|
53
54
|
- **Transcripts**: inspect local Claude and Codex transcript files from the terminal.
|
|
54
|
-
- **Knowledges**: search and read the hosted Harness knowledge catalog.
|
|
55
|
-
- **
|
|
55
|
+
- **Knowledges**: traverse, search, and read the hosted Harness knowledge catalog.
|
|
56
|
+
- **Hooks**: install Hookify forwarding hooks into supported AI-assisted coding tools.
|
|
57
|
+
- **Hookify**: forward local hook events for server-side hook processing.
|
|
56
58
|
|
|
57
59
|
## Coderoom
|
|
58
60
|
|
|
59
61
|
Coderoom is a live room for quickly sharing a working tree with another developer or agent.
|
|
60
|
-
One machine starts a room, another joins with the room code, and the
|
|
62
|
+
One machine starts a room, another joins with the room code, and the backend relays
|
|
61
63
|
file changes.
|
|
62
64
|
|
|
63
65
|
Start a room:
|
|
@@ -84,7 +86,7 @@ received change wins.
|
|
|
84
86
|
|
|
85
87
|
## Harness Knowledges
|
|
86
88
|
|
|
87
|
-
The `knowledges` commands read the Cloudflare-backed Harness catalog
|
|
89
|
+
The `knowledges` commands read the Cloudflare-backed Harness catalog. They are
|
|
88
90
|
read-only and designed for AI-friendly terminal use.
|
|
89
91
|
|
|
90
92
|
Check catalog health:
|
|
@@ -93,6 +95,17 @@ Check catalog health:
|
|
|
93
95
|
cngkit knowledges status
|
|
94
96
|
```
|
|
95
97
|
|
|
98
|
+
Browse the catalog like a remote filesystem:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
cngkit knowledges ls /
|
|
102
|
+
cngkit knowledges ls /libraries/lib-cloudflare -l
|
|
103
|
+
cngkit knowledges tree /libraries --depth 2
|
|
104
|
+
cngkit knowledges ls /libraries/lib-cloudflare
|
|
105
|
+
cngkit knowledges stat /libraries/lib-cloudflare/TOPIC.md
|
|
106
|
+
cngkit knowledges realpath /libraries/lib-cloudflare
|
|
107
|
+
```
|
|
108
|
+
|
|
96
109
|
Find relevant knowledge:
|
|
97
110
|
|
|
98
111
|
```bash
|
|
@@ -102,7 +115,16 @@ cngkit knowledges search Cloudflare --limit 3
|
|
|
102
115
|
Read a catalog file:
|
|
103
116
|
|
|
104
117
|
```bash
|
|
105
|
-
cngkit knowledges
|
|
118
|
+
cngkit knowledges cat /libraries/lib-cloudflare/TOPIC.md
|
|
119
|
+
cngkit knowledges head /libraries/lib-cloudflare/TOPIC.md -n 20
|
|
120
|
+
cngkit knowledges tail /libraries/lib-cloudflare/TOPIC.md -n 20
|
|
121
|
+
cngkit knowledges read /libraries/lib-cloudflare/TOPIC.md --limit 80
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Find paths by shell-style filters:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
cngkit knowledges find /libraries -name "*cloudflare*" -type f
|
|
106
128
|
```
|
|
107
129
|
|
|
108
130
|
List matching files:
|
|
@@ -121,21 +143,37 @@ Return JSON for another tool:
|
|
|
121
143
|
|
|
122
144
|
```bash
|
|
123
145
|
cngkit knowledges status --json
|
|
146
|
+
cngkit --format json knowledges ls /
|
|
147
|
+
cngkit --format json knowledges tree /libraries --depth 2
|
|
148
|
+
cngkit --format json knowledges find /libraries -name "*cloudflare*" -type f
|
|
149
|
+
cngkit --format json knowledges stat /libraries/lib-cloudflare/TOPIC.md
|
|
124
150
|
cngkit --format json knowledges search "vector search" --limit 5
|
|
125
151
|
```
|
|
126
152
|
|
|
127
|
-
Catalog
|
|
153
|
+
Catalog paths are rooted at the Harness topics folder. For example:
|
|
128
154
|
|
|
129
155
|
```text
|
|
130
|
-
/libraries/lib-cloudflare/
|
|
156
|
+
/libraries/lib-cloudflare/TOPIC.md
|
|
131
157
|
```
|
|
132
158
|
|
|
133
|
-
|
|
159
|
+
means:
|
|
134
160
|
|
|
135
161
|
```text
|
|
136
|
-
|
|
162
|
+
~/.agents/topics/libraries/lib-cloudflare/TOPIC.md
|
|
137
163
|
```
|
|
138
164
|
|
|
165
|
+
The `knowledges` command set is stateless. It does not keep a working directory and does
|
|
166
|
+
not provide interactive traversal commands; pass the path explicitly on each command.
|
|
167
|
+
|
|
168
|
+
Text output is shell-friendly:
|
|
169
|
+
|
|
170
|
+
- `ls` prints one entry name per line; directories end with `/`. Use `--long` or `-l`
|
|
171
|
+
for the older metadata-heavy tab-separated output.
|
|
172
|
+
- `tree` prints a deterministic plain-text tree; directories end with `/`.
|
|
173
|
+
- `cat`, `head`, and `tail` print raw file content only.
|
|
174
|
+
- `find`, `glob`, and `grep --output-mode files_with_matches` print one path per line.
|
|
175
|
+
- `read`, `glob`, and `realpath` remain useful agent/API-oriented helper commands.
|
|
176
|
+
|
|
139
177
|
## Secret Scrubbing
|
|
140
178
|
|
|
141
179
|
`scrub` scans a file or directory with TruffleHog and prints a redacted report.
|
|
@@ -172,17 +210,38 @@ Safety notes:
|
|
|
172
210
|
|
|
173
211
|
## Hookify
|
|
174
212
|
|
|
175
|
-
`
|
|
176
|
-
|
|
213
|
+
Use `hooks install` to configure supported local AI-assisted coding tools:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
cngkit hooks install --dry-run
|
|
217
|
+
cngkit hooks install
|
|
218
|
+
cngkit hooks install --tool codex
|
|
219
|
+
cngkit hooks uninstall --dry-run
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The installer currently writes user-level hook configuration for Claude Code and Codex.
|
|
223
|
+
It preserves existing non-cngkit hook handlers, replaces older cngkit Hookify handlers,
|
|
224
|
+
and installs `cngkit hookify ingest --event <EventName>` for every supported hook event
|
|
225
|
+
in each tool. Hook commands use the already-installed `cngkit` binary so hook execution
|
|
226
|
+
does not resolve `cngkit@latest` through npm on every event.
|
|
227
|
+
`hooks uninstall` removes current direct cngkit Hookify handlers and older npm/npx
|
|
228
|
+
`cngkit@latest` handlers, then reports the exact files, handler counts, and event names
|
|
229
|
+
it changed.
|
|
230
|
+
|
|
231
|
+
`hookify` is the hosted hook processing surface. The first command is intentionally
|
|
232
|
+
small: it reads stdin and forwards the raw payload to the backend. The backend can return
|
|
233
|
+
the hook result immediately or return a request id for workflow-backed processing; the CLI
|
|
234
|
+
polls until the final stdout, stderr, and exit code are ready.
|
|
177
235
|
|
|
178
236
|
```bash
|
|
179
237
|
cngkit hookify ingest --event PreToolUse < hook-payload.json
|
|
180
238
|
cngkit hookify ingest --event Stop --async < hook-payload.json
|
|
181
239
|
```
|
|
182
240
|
|
|
183
|
-
The backend response controls stdout and the process exit code.
|
|
184
|
-
|
|
185
|
-
the
|
|
241
|
+
The backend response controls stdout and the process exit code. `--async` means the server
|
|
242
|
+
may defer the result behind a request id; it does not mean the local hook returns before the
|
|
243
|
+
final result is known. If the backend request or polling fails, the command falls back to
|
|
244
|
+
exit code `0` so local hooks do not block work because the remote service is unavailable.
|
|
186
245
|
|
|
187
246
|
## Local Agent Transcripts
|
|
188
247
|
|
|
@@ -211,7 +270,7 @@ Search recent transcript entries:
|
|
|
211
270
|
cngkit transcripts grep "deploy failed" --source all --file-limit 60 --limit 20
|
|
212
271
|
```
|
|
213
272
|
|
|
214
|
-
Transcript commands are local-only. They do not upload transcript content to
|
|
273
|
+
Transcript commands are local-only. They do not upload transcript content to the backend.
|
|
215
274
|
By default, they print user and assistant text and skip internal prompt, hook, and tool noise.
|
|
216
275
|
Use `--include-internal` when debugging transcript plumbing.
|
|
217
276
|
|
|
@@ -232,7 +291,7 @@ source text:
|
|
|
232
291
|
```bash
|
|
233
292
|
cngkit --format text --help
|
|
234
293
|
cngkit --format json knowledges status
|
|
235
|
-
cngkit --format markdown knowledges read /libraries/lib-cloudflare/
|
|
294
|
+
cngkit --format markdown knowledges read /libraries/lib-cloudflare/TOPIC.md
|
|
236
295
|
cngkit --no-color knowledges status
|
|
237
296
|
```
|
|
238
297
|
|
|
@@ -251,6 +310,9 @@ cngkit help knowledges
|
|
|
251
310
|
cngkit coderoom --help
|
|
252
311
|
cngkit knowledges read --help
|
|
253
312
|
cngkit transcripts --help
|
|
313
|
+
cngkit hooks --help
|
|
314
|
+
cngkit hooks install --help
|
|
315
|
+
cngkit hooks uninstall --help
|
|
254
316
|
cngkit hookify --help
|
|
255
317
|
cngkit hookify ingest --help
|
|
256
318
|
```
|
|
@@ -280,9 +342,22 @@ src/commands/
|
|
|
280
342
|
share.tsx cngkit coderoom share
|
|
281
343
|
join.tsx cngkit coderoom join
|
|
282
344
|
knowledges/
|
|
345
|
+
ls.tsx cngkit knowledges ls
|
|
346
|
+
tree.tsx cngkit knowledges tree
|
|
347
|
+
cat.tsx cngkit knowledges cat
|
|
348
|
+
head.tsx cngkit knowledges head
|
|
349
|
+
tail.tsx cngkit knowledges tail
|
|
350
|
+
find.tsx cngkit knowledges find
|
|
351
|
+
stat.tsx cngkit knowledges stat
|
|
352
|
+
realpath.tsx cngkit knowledges realpath
|
|
283
353
|
read.tsx cngkit knowledges read
|
|
284
354
|
grep.tsx cngkit knowledges grep
|
|
285
355
|
glob.tsx cngkit knowledges glob
|
|
356
|
+
hooks/
|
|
357
|
+
install.tsx cngkit hooks install
|
|
358
|
+
uninstall.tsx cngkit hooks uninstall
|
|
359
|
+
hookify/
|
|
360
|
+
ingest.tsx cngkit hookify ingest
|
|
286
361
|
```
|
|
287
362
|
|
|
288
363
|
The CLI does not hand-roll ANSI escape codes. Status styles go through Ink's built-in
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CngApiClient,
|
|
3
|
+
formatError,
|
|
4
|
+
resolveApiBaseUrl
|
|
5
|
+
} from "./chunk-WDI43VPW.js";
|
|
6
|
+
|
|
7
|
+
// src/shared/api-client.ts
|
|
8
|
+
function createCngApiClient(options) {
|
|
9
|
+
return new CngApiClient({
|
|
10
|
+
baseUrl: resolveApiBaseUrl(options),
|
|
11
|
+
timeoutInSeconds: 15,
|
|
12
|
+
maxRetries: 1
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function readBackendHealth(options) {
|
|
16
|
+
try {
|
|
17
|
+
const client = createCngApiClient(options);
|
|
18
|
+
const health = await client.system.getHealth();
|
|
19
|
+
return {
|
|
20
|
+
ok: true,
|
|
21
|
+
service: health.service
|
|
22
|
+
};
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
message: formatError(error)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
createCngApiClient,
|
|
33
|
+
readBackendHealth
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chunk-CIZBVLN5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/api-client.ts"],"sourcesContent":["import { CngApiClient } from \"@cng/client\";\n\nimport { resolveApiBaseUrl, type GlobalCommandOptions } from \"./config.js\";\nimport { formatError } from \"./output.js\";\n\nexport type BackendHealthStatus =\n | {\n ok: true;\n service: string;\n }\n | {\n ok: false;\n message: string;\n };\n\nexport function createCngApiClient(options: GlobalCommandOptions): CngApiClient {\n return new CngApiClient({\n baseUrl: resolveApiBaseUrl(options),\n timeoutInSeconds: 15,\n maxRetries: 1,\n });\n}\n\nexport async function readBackendHealth(\n options: GlobalCommandOptions\n): Promise<BackendHealthStatus> {\n try {\n const client = createCngApiClient(options);\n const health = await client.system.getHealth();\n return {\n ok: true,\n service: health.service,\n };\n } catch (error) {\n return {\n ok: false,\n message: formatError(error),\n };\n }\n}\n"],"mappings":";;;;;;;AAeO,SAAS,mBAAmB,SAA6C;AAC9E,SAAO,IAAI,aAAa;AAAA,IACtB,SAAS,kBAAkB,OAAO;AAAA,IAClC,kBAAkB;AAAA,IAClB,YAAY;AAAA,EACd,CAAC;AACH;AAEA,eAAsB,kBACpB,SAC8B;AAC9B,MAAI;AACF,UAAM,SAAS,mBAAmB,OAAO;AACzC,UAAM,SAAS,MAAM,OAAO,OAAO,UAAU;AAC7C,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,SAAS,OAAO;AAAA,IAClB;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,SAAS,YAAY,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readBackendHealth
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CIZBVLN5.js";
|
|
4
4
|
import {
|
|
5
5
|
createPeerId,
|
|
6
6
|
createRoomCode,
|
|
7
7
|
resolveApiBaseUrl
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WDI43VPW.js";
|
|
9
9
|
|
|
10
10
|
// src/features/coderoom/run-coderoom-command.ts
|
|
11
11
|
import process2 from "process";
|
|
12
12
|
|
|
13
13
|
// src/features/coderoom/sync/client.ts
|
|
14
14
|
import process from "process";
|
|
15
|
+
import fs2 from "fs";
|
|
15
16
|
import chokidar from "chokidar";
|
|
16
17
|
import WebSocket from "ws";
|
|
17
18
|
|
|
@@ -263,7 +264,15 @@ function createRepoWatcher(context) {
|
|
|
263
264
|
ignoreInitial: true,
|
|
264
265
|
ignored: (candidatePath) => {
|
|
265
266
|
const relativePath = toRepoRelativePath(context.repoContext.rootDir, candidatePath);
|
|
266
|
-
|
|
267
|
+
if (relativePath?.split("/").includes(".git") ?? false) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const stat = fs2.statSync(candidatePath);
|
|
272
|
+
return !stat.isFile() && !stat.isDirectory();
|
|
273
|
+
} catch {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
267
276
|
}
|
|
268
277
|
});
|
|
269
278
|
watcher.on("add", (absolutePath) => {
|
|
@@ -275,6 +284,9 @@ function createRepoWatcher(context) {
|
|
|
275
284
|
watcher.on("unlink", (absolutePath) => {
|
|
276
285
|
void sendLocalDelete(context, absolutePath);
|
|
277
286
|
});
|
|
287
|
+
watcher.on("error", (watcherError) => {
|
|
288
|
+
context.output.warning(`watcher error: ${watcherError instanceof Error ? watcherError.message : String(watcherError)}`);
|
|
289
|
+
});
|
|
278
290
|
return watcher;
|
|
279
291
|
}
|
|
280
292
|
async function sendInitialSnapshot(socket, repoContext, peerId, output) {
|
|
@@ -412,4 +424,4 @@ export {
|
|
|
412
424
|
runShareCommand,
|
|
413
425
|
runJoinCommand
|
|
414
426
|
};
|
|
415
|
-
//# sourceMappingURL=chunk-
|
|
427
|
+
//# sourceMappingURL=chunk-E2GLGGKO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/features/coderoom/run-coderoom-command.ts","../src/features/coderoom/sync/client.ts","../src/features/coderoom/sync/files.ts","../src/features/coderoom/sync/paths.ts","../src/features/coderoom/sync/protocol.ts"],"sourcesContent":["import process from \"node:process\";\n\nimport { readBackendHealth } from \"../../shared/api-client.js\";\nimport { createRoomCode, resolveApiBaseUrl, type GlobalCommandOptions } from \"../../shared/config.js\";\nimport type { CommandOutput } from \"../../shared/output.js\";\nimport { startSyncSession, type SyncSessionRole } from \"./sync/client.js\";\n\nexport type CoderoomCommandOptions = GlobalCommandOptions;\n\nexport type ShareCommandOptions = GlobalCommandOptions;\n\nexport async function runCoderoomCommand(\n args: string[] | undefined,\n options: CoderoomCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const [subcommand, ...subcommandArgs] = args ?? [];\n\n switch (subcommand) {\n case \"share\":\n return runShareCommand(subcommandArgs[0], options, output);\n case \"join\":\n return runJoinCommand(subcommandArgs[0], options, output);\n default:\n throw new Error(\"Missing coderoom command. Usage: cngkit coderoom <share|join>\");\n }\n}\n\nexport async function runShareCommand(\n roomCode: string | undefined,\n options: ShareCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const syncRoomCode = roomCode ?? createRoomCode();\n\n output.success(`Share code: ${syncRoomCode}`);\n await printBackendStatus(options, output);\n await runSyncSession(\"share\", syncRoomCode, options, output);\n}\n\nexport async function runJoinCommand(\n roomCode: string | undefined,\n options: GlobalCommandOptions,\n output: CommandOutput\n): Promise<void> {\n if (!roomCode) {\n throw new Error(\"Missing room code. Usage: cngkit coderoom join <room-code>\");\n }\n\n await printBackendStatus(options, output);\n await runSyncSession(\"join\", roomCode, options, output);\n}\n\nasync function printBackendStatus(\n options: GlobalCommandOptions,\n output: CommandOutput\n): Promise<void> {\n const health = await readBackendHealth(options);\n if (health.ok) {\n output.success(`API: ${health.service} ready`);\n return;\n }\n\n output.warning(`API: unavailable (${health.message})`);\n}\n\nasync function runSyncSession(\n role: SyncSessionRole,\n roomCode: string,\n options: GlobalCommandOptions,\n output: CommandOutput\n): Promise<void> {\n await startSyncSession({\n apiBaseUrl: resolveApiBaseUrl(options),\n roomCode,\n role,\n cwd: process.cwd(),\n output,\n });\n}\n","import process from \"node:process\";\nimport fs from \"node:fs\";\n\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport WebSocket, { type RawData } from \"ws\";\n\nimport { createPeerId } from \"../../../shared/config.js\";\nimport type { CommandOutput } from \"../../../shared/output.js\";\nimport {\n applyRemoteMessage,\n buildFileMessage,\n collectSnapshotMessages,\n createSuppressionTracker,\n type SuppressionTracker,\n} from \"./files.js\";\nimport {\n resolveRepoContext,\n shouldSyncRelativePath,\n toRepoRelativePath,\n type RepoContext,\n} from \"./paths.js\";\nimport { decodeSyncMessage, encodeSyncMessage, type SyncMessage } from \"./protocol.js\";\n\nexport type SyncSessionRole = \"share\" | \"join\";\n\nexport type StartSyncSessionOptions = {\n apiBaseUrl: string;\n roomCode: string;\n role: SyncSessionRole;\n cwd: string;\n output: CommandOutput;\n};\n\ntype WatcherContext = {\n repoContext: RepoContext;\n socket: WebSocket;\n peerId: string;\n suppressionTracker: SuppressionTracker;\n output: CommandOutput;\n};\n\nfunction createSyncWebSocketUrl(apiBaseUrl: string, roomCode: string): string {\n const url = new URL(apiBaseUrl);\n url.protocol = url.protocol === \"http:\" ? \"ws:\" : \"wss:\";\n url.pathname = `/api/cng/sync/${encodeURIComponent(roomCode)}`;\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n}\n\nfunction sendMessage(socket: WebSocket, message: SyncMessage): void {\n if (socket.readyState === WebSocket.OPEN) {\n socket.send(encodeSyncMessage(message));\n }\n}\n\nexport async function startSyncSession(options: StartSyncSessionOptions): Promise<void> {\n const repoContext = await resolveRepoContext(options.cwd);\n const peerId = createPeerId();\n const suppressionTracker = createSuppressionTracker();\n const webSocketUrl = createSyncWebSocketUrl(options.apiBaseUrl, options.roomCode);\n const socket = new WebSocket(webSocketUrl);\n\n options.output.success(`Room: ${options.roomCode}`);\n options.output.info(`Repo: ${repoContext.rootDir}`);\n options.output.muted(`Peer: ${peerId}`);\n\n const watcherContext: WatcherContext = {\n repoContext,\n socket,\n peerId,\n suppressionTracker,\n output: options.output,\n };\n const watcher = createRepoWatcher(watcherContext);\n\n socket.on(\"open\", () => {\n sendMessage(socket, {\n type: \"hello\",\n peerId,\n role: options.role,\n sentAt: Date.now(),\n });\n void sendInitialSnapshot(socket, repoContext, peerId, options.output);\n });\n\n socket.on(\"message\", (data) => {\n void handleRemoteMessage({\n data,\n repoContext,\n peerId,\n suppressionTracker,\n output: options.output,\n });\n });\n\n await waitForSessionClose(socket, watcher);\n}\n\nfunction createRepoWatcher(context: WatcherContext): FSWatcher {\n const watcher = chokidar.watch(context.repoContext.rootDir, {\n ignoreInitial: true,\n ignored: (candidatePath) => {\n const relativePath = toRepoRelativePath(context.repoContext.rootDir, candidatePath);\n if (relativePath?.split(\"/\").includes(\".git\") ?? false) {\n return true;\n }\n try {\n const stat = fs.statSync(candidatePath);\n return !stat.isFile() && !stat.isDirectory();\n } catch {\n return false;\n }\n },\n });\n\n watcher.on(\"add\", (absolutePath) => {\n void sendLocalFileChange(context, absolutePath);\n });\n watcher.on(\"change\", (absolutePath) => {\n void sendLocalFileChange(context, absolutePath);\n });\n watcher.on(\"unlink\", (absolutePath) => {\n void sendLocalDelete(context, absolutePath);\n });\n watcher.on(\"error\", (watcherError) => {\n context.output.warning(`watcher error: ${watcherError instanceof Error ? watcherError.message : String(watcherError)}`);\n });\n\n return watcher;\n}\n\nasync function sendInitialSnapshot(\n socket: WebSocket,\n repoContext: RepoContext,\n peerId: string,\n output: CommandOutput\n): Promise<void> {\n let fileCount = 0;\n\n for await (const message of collectSnapshotMessages(repoContext, peerId)) {\n sendMessage(socket, message);\n fileCount += 1;\n }\n\n sendMessage(socket, {\n type: \"snapshot-complete\",\n peerId,\n sentAt: Date.now(),\n fileCount,\n });\n output.success(`sent snapshot ${fileCount} files`);\n}\n\nasync function sendLocalFileChange(context: WatcherContext, absolutePath: string): Promise<void> {\n const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);\n if (!relativePath || context.suppressionTracker.isSuppressed(relativePath)) {\n return;\n }\n\n const message = await buildFileMessage(context.repoContext, context.peerId, relativePath);\n if (!message) {\n return;\n }\n\n sendMessage(context.socket, message);\n context.output.muted(`sent file ${relativePath}`);\n}\n\nasync function sendLocalDelete(context: WatcherContext, absolutePath: string): Promise<void> {\n const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);\n if (!relativePath || context.suppressionTracker.isSuppressed(relativePath)) {\n return;\n }\n\n if (!(await shouldSyncRelativePath(context.repoContext, relativePath))) {\n return;\n }\n\n sendMessage(context.socket, {\n type: \"delete\",\n peerId: context.peerId,\n path: relativePath,\n mtimeMs: Date.now(),\n sentAt: Date.now(),\n });\n context.output.warning(`sent delete ${relativePath}`);\n}\n\nasync function handleRemoteMessage(options: {\n data: RawData;\n repoContext: RepoContext;\n peerId: string;\n suppressionTracker: SuppressionTracker;\n output: CommandOutput;\n}): Promise<void> {\n const decodedMessage = decodeSyncMessage(options.data.toString());\n if (!decodedMessage || decodedMessage.peerId === options.peerId) {\n return;\n }\n\n if (decodedMessage.type === \"hello\") {\n options.output.info(`peer joined ${decodedMessage.peerId}`);\n return;\n }\n\n if (decodedMessage.type === \"snapshot-complete\") {\n options.output.success(`peer snapshot complete ${decodedMessage.fileCount} files`);\n return;\n }\n\n await applyRemoteMessage(options.repoContext, decodedMessage, options.suppressionTracker);\n options.output.success(`applied ${decodedMessage.type} ${decodedMessage.path}`);\n}\n\nasync function waitForSessionClose(socket: WebSocket, watcher: FSWatcher): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n\n const closeSession = async (): Promise<void> => {\n await watcher.close();\n if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {\n socket.close();\n }\n };\n\n const finish = async (): Promise<void> => {\n if (settled) {\n return;\n }\n settled = true;\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n await closeSession();\n resolve();\n };\n\n const onSignal = (): void => {\n void finish();\n };\n\n socket.on(\"error\", async (error) => {\n if (settled) {\n return;\n }\n settled = true;\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n await closeSession();\n reject(error);\n });\n\n socket.on(\"close\", () => {\n void finish();\n });\n\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n });\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { SyncFileMutationMessage, SyncMessage } from \"./protocol.js\";\nimport {\n resolveRepoPath,\n shouldSyncRelativePath,\n toRepoRelativePath,\n type RepoContext,\n} from \"./paths.js\";\n\nexport type SuppressionTracker = {\n suppress(relativePath: string): void;\n isSuppressed(relativePath: string): boolean;\n};\n\nexport function createSuppressionTracker(windowMs = 1500): SuppressionTracker {\n const suppressedUntilByPath = new Map<string, number>();\n\n return {\n suppress(relativePath: string) {\n suppressedUntilByPath.set(relativePath, Date.now() + windowMs);\n },\n isSuppressed(relativePath: string) {\n const suppressedUntil = suppressedUntilByPath.get(relativePath);\n if (!suppressedUntil) {\n return false;\n }\n if (suppressedUntil < Date.now()) {\n suppressedUntilByPath.delete(relativePath);\n return false;\n }\n return true;\n },\n };\n}\n\nexport async function* collectSnapshotMessages(\n context: RepoContext,\n peerId: string\n): AsyncGenerator<SyncMessage> {\n yield* collectDirectorySnapshot(context, context.rootDir, peerId);\n}\n\nasync function* collectDirectorySnapshot(\n context: RepoContext,\n directoryPath: string,\n peerId: string\n): AsyncGenerator<SyncMessage> {\n const entries = await fs.opendir(directoryPath);\n\n for await (const entry of entries) {\n const absolutePath = path.join(directoryPath, entry.name);\n const relativePath = toRepoRelativePath(context.rootDir, absolutePath);\n\n if (!relativePath || !(await shouldSyncRelativePath(context, relativePath))) {\n continue;\n }\n\n if (entry.isDirectory()) {\n yield* collectDirectorySnapshot(context, absolutePath, peerId);\n continue;\n }\n\n if (!entry.isFile()) {\n continue;\n }\n\n const [content, stat] = await Promise.all([fs.readFile(absolutePath), fs.stat(absolutePath)]);\n yield {\n type: \"file\",\n peerId,\n path: relativePath,\n contentBase64: content.toString(\"base64\"),\n mtimeMs: stat.mtimeMs,\n sentAt: Date.now(),\n };\n }\n}\n\nexport async function buildFileMessage(\n context: RepoContext,\n peerId: string,\n relativePath: string\n): Promise<SyncMessage | undefined> {\n if (!(await shouldSyncRelativePath(context, relativePath))) {\n return undefined;\n }\n\n const absolutePath = resolveRepoPath(context.rootDir, relativePath);\n if (!absolutePath) {\n return undefined;\n }\n\n const stat = await fs.stat(absolutePath);\n if (!stat.isFile()) {\n return undefined;\n }\n\n const content = await fs.readFile(absolutePath);\n return {\n type: \"file\",\n peerId,\n path: relativePath,\n contentBase64: content.toString(\"base64\"),\n mtimeMs: stat.mtimeMs,\n sentAt: Date.now(),\n };\n}\n\nexport async function applyRemoteMessage(\n context: RepoContext,\n message: SyncFileMutationMessage,\n suppressionTracker: SuppressionTracker\n): Promise<void> {\n const absolutePath = resolveRepoPath(context.rootDir, message.path);\n if (!absolutePath || !(await shouldSyncRelativePath(context, message.path))) {\n return;\n }\n\n suppressionTracker.suppress(message.path);\n\n if (message.type === \"delete\") {\n await fs.rm(absolutePath, { force: true });\n return;\n }\n\n await fs.mkdir(path.dirname(absolutePath), { recursive: true });\n await fs.writeFile(absolutePath, Buffer.from(message.contentBase64, \"base64\"));\n}\n","import { execFile } from \"node:child_process\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nexport type RepoContext = {\n rootDir: string;\n};\n\nexport async function resolveRepoContext(cwd: string): Promise<RepoContext> {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd,\n });\n return { rootDir: path.resolve(stdout.trim()) };\n } catch {\n return { rootDir: path.resolve(cwd) };\n }\n}\n\nexport function toRepoRelativePath(rootDir: string, absolutePath: string): string | undefined {\n const relativePath = path.relative(rootDir, absolutePath);\n\n if (!relativePath || relativePath.startsWith(\"..\") || path.isAbsolute(relativePath)) {\n return undefined;\n }\n\n return relativePath.split(path.sep).join(\"/\");\n}\n\nexport function resolveRepoPath(rootDir: string, relativePath: string): string | undefined {\n if (!isSafeRelativePath(relativePath)) {\n return undefined;\n }\n\n const absolutePath = path.resolve(rootDir, relativePath);\n const normalizedRoot = `${path.resolve(rootDir)}${path.sep}`;\n\n if (absolutePath !== path.resolve(rootDir) && !absolutePath.startsWith(normalizedRoot)) {\n return undefined;\n }\n\n return absolutePath;\n}\n\nexport function isSafeRelativePath(relativePath: string): boolean {\n if (!relativePath || relativePath.startsWith(\"/\") || relativePath.includes(\"\\0\")) {\n return false;\n }\n\n const normalizedParts = relativePath.split(/[\\\\/]+/).filter(Boolean);\n if (normalizedParts.includes(\"..\")) {\n return false;\n }\n\n return normalizedParts[0] !== \".git\";\n}\n\nexport async function shouldSyncRelativePath(\n context: RepoContext,\n relativePath: string\n): Promise<boolean> {\n if (!isSafeRelativePath(relativePath)) {\n return false;\n }\n\n try {\n await execFileAsync(\"git\", [\"check-ignore\", \"--quiet\", \"--\", relativePath], {\n cwd: context.rootDir,\n });\n return false;\n } catch (error) {\n const exitCode =\n typeof error === \"object\" && error !== null && \"code\" in error ? error.code : undefined;\n return exitCode === 1;\n }\n}\n","import { z } from \"zod\";\n\nconst SyncBaseMessageSchema = z.object({\n peerId: z.string().min(1),\n sentAt: z.number().finite(),\n});\n\nexport const SyncHelloMessageSchema = SyncBaseMessageSchema.extend({\n type: z.literal(\"hello\"),\n role: z.union([z.literal(\"share\"), z.literal(\"join\")]),\n});\n\nexport const SyncFileMessageSchema = SyncBaseMessageSchema.extend({\n type: z.literal(\"file\"),\n path: z.string().min(1),\n contentBase64: z.string(),\n mtimeMs: z.number().finite(),\n});\n\nexport const SyncDeleteMessageSchema = SyncBaseMessageSchema.extend({\n type: z.literal(\"delete\"),\n path: z.string().min(1),\n mtimeMs: z.number().finite(),\n});\n\nexport const SyncSnapshotCompleteMessageSchema = SyncBaseMessageSchema.extend({\n type: z.literal(\"snapshot-complete\"),\n fileCount: z.number().int().nonnegative(),\n});\n\nexport const SyncMessageSchema = z.discriminatedUnion(\"type\", [\n SyncHelloMessageSchema,\n SyncFileMessageSchema,\n SyncDeleteMessageSchema,\n SyncSnapshotCompleteMessageSchema,\n]);\n\nexport type SyncMessage = z.infer<typeof SyncMessageSchema>;\nexport type SyncFileMutationMessage = Extract<SyncMessage, { type: \"file\" | \"delete\" }>;\n\nexport function encodeSyncMessage(message: SyncMessage): string {\n return JSON.stringify(SyncMessageSchema.parse(message));\n}\n\nexport function decodeSyncMessage(value: unknown): SyncMessage | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n try {\n return SyncMessageSchema.parse(JSON.parse(value));\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":["process","fs","path","path","fs","process"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandOutputMessages,
|
|
3
|
+
createCommandOutput,
|
|
4
|
+
formatError
|
|
5
|
+
} from "./chunk-WDI43VPW.js";
|
|
6
|
+
|
|
7
|
+
// src/cli/command-runner.tsx
|
|
8
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
|
+
import { useApp } from "ink";
|
|
10
|
+
import { jsx } from "react/jsx-runtime";
|
|
11
|
+
function CommandRunner({ run }) {
|
|
12
|
+
const { exit } = useApp();
|
|
13
|
+
const nextMessageIdRef = useRef(1);
|
|
14
|
+
const [messages, setMessages] = useState([]);
|
|
15
|
+
const appendMessage = useCallback((message) => {
|
|
16
|
+
const nextMessage = {
|
|
17
|
+
...message,
|
|
18
|
+
id: nextMessageIdRef.current
|
|
19
|
+
};
|
|
20
|
+
nextMessageIdRef.current += 1;
|
|
21
|
+
setMessages((currentMessages) => [...currentMessages, nextMessage]);
|
|
22
|
+
}, []);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const output = createCommandOutput({
|
|
25
|
+
appendMessage,
|
|
26
|
+
stdout: process.stdout,
|
|
27
|
+
stderr: process.stderr
|
|
28
|
+
});
|
|
29
|
+
void run(output).catch((error) => {
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
output.error(formatError(error));
|
|
32
|
+
}).finally(() => {
|
|
33
|
+
setTimeout(() => exit(), 0);
|
|
34
|
+
});
|
|
35
|
+
}, [appendMessage, exit, run]);
|
|
36
|
+
return /* @__PURE__ */ jsx(CommandOutputMessages, { messages });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
CommandRunner
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=chunk-MRXGD6TC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/command-runner.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useApp } from \"ink\";\n\nimport {\n CommandOutputMessages,\n createCommandOutput,\n formatError,\n type CommandOutput,\n type CommandOutputMessage,\n type CommandOutputMessagePayload,\n} from \"../shared/output.js\";\n\ntype CommandRunnerProps = {\n readonly run: (output: CommandOutput) => Promise<void>;\n};\n\nexport function CommandRunner({ run }: CommandRunnerProps) {\n const { exit } = useApp();\n const nextMessageIdRef = useRef(1);\n const [messages, setMessages] = useState<CommandOutputMessage[]>([]);\n const appendMessage = useCallback((message: CommandOutputMessagePayload) => {\n const nextMessage: CommandOutputMessage = {\n ...message,\n id: nextMessageIdRef.current,\n };\n nextMessageIdRef.current += 1;\n setMessages((currentMessages) => [...currentMessages, nextMessage]);\n }, []);\n\n useEffect(() => {\n const output = createCommandOutput({\n appendMessage,\n stdout: process.stdout,\n stderr: process.stderr,\n });\n\n void run(output)\n .catch((error: unknown) => {\n process.exitCode = 1;\n output.error(formatError(error));\n })\n .finally(() => {\n setTimeout(() => exit(), 0);\n });\n }, [appendMessage, exit, run]);\n\n return <CommandOutputMessages messages={messages} />;\n}\n"],"mappings":";;;;;;;AAAA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AACzD,SAAS,cAAc;AA6Cd;AA9BF,SAAS,cAAc,EAAE,IAAI,GAAuB;AACzD,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,mBAAmB,OAAO,CAAC;AACjC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAiC,CAAC,CAAC;AACnE,QAAM,gBAAgB,YAAY,CAAC,YAAyC;AAC1E,UAAM,cAAoC;AAAA,MACxC,GAAG;AAAA,MACH,IAAI,iBAAiB;AAAA,IACvB;AACA,qBAAiB,WAAW;AAC5B,gBAAY,CAAC,oBAAoB,CAAC,GAAG,iBAAiB,WAAW,CAAC;AAAA,EACpE,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,UAAM,SAAS,oBAAoB;AAAA,MACjC;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,SAAK,IAAI,MAAM,EACZ,MAAM,CAAC,UAAmB;AACzB,cAAQ,WAAW;AACnB,aAAO,MAAM,YAAY,KAAK,CAAC;AAAA,IACjC,CAAC,EACA,QAAQ,MAAM;AACb,iBAAW,MAAM,KAAK,GAAG,CAAC;AAAA,IAC5B,CAAC;AAAA,EACL,GAAG,CAAC,eAAe,MAAM,GAAG,CAAC;AAE7B,SAAO,oBAAC,yBAAsB,UAAoB;AACpD;","names":[]}
|
|
@@ -107,9 +107,10 @@ export {
|
|
|
107
107
|
OptionalQueryArgsSchema,
|
|
108
108
|
RequiredQueryArgsSchema,
|
|
109
109
|
RequiredFilePathArgsSchema,
|
|
110
|
+
RequiredCatalogPathArgsSchema,
|
|
110
111
|
RequiredPatternArgsSchema,
|
|
111
112
|
OptionalGlobPatternArgsSchema,
|
|
112
113
|
LimitOptionsSchema,
|
|
113
114
|
TranscriptArgsSchema
|
|
114
115
|
};
|
|
115
|
-
//# sourceMappingURL=chunk-
|
|
116
|
+
//# sourceMappingURL=chunk-NGEWD4BW.js.map
|