cngkit 1.1.7 → 1.1.8
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 +249 -82
- package/dist/{chunk-Z4DDLEWR.js → chunk-CELXWAUR.js} +4 -12
- package/dist/chunk-CELXWAUR.js.map +1 -0
- package/dist/{chunk-HUZZPV5E.js → chunk-FIRWCD2M.js} +16 -12
- package/dist/chunk-FIRWCD2M.js.map +1 -0
- package/dist/{chunk-QEZQGKFX.js → chunk-PXSVLAAG.js} +2 -2
- package/dist/{chunk-TZKXST4G.js → chunk-SFZS5K6B.js} +48 -20
- package/dist/chunk-SFZS5K6B.js.map +1 -0
- package/dist/{chunk-VI5XQH3U.js → chunk-WPBMAXXM.js} +3 -3
- package/dist/{chunk-VI5XQH3U.js.map → chunk-WPBMAXXM.js.map} +1 -1
- package/dist/{chunk-EQEIX7N5.js → chunk-WR4HJ57I.js} +2 -2
- package/dist/{chunk-EQEIX7N5.js.map → chunk-WR4HJ57I.js.map} +1 -1
- package/dist/chunk-X3THKPHT.js +83 -0
- package/dist/chunk-X3THKPHT.js.map +1 -0
- package/dist/cli.js +8 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/coderoom/index.js +4 -4
- 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/index.js +4 -4
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/knowledges/audiences.js +6 -6
- package/dist/commands/knowledges/files.js +6 -6
- package/dist/commands/knowledges/glob.js +6 -6
- package/dist/commands/knowledges/grep.js +6 -6
- package/dist/commands/knowledges/index.js +4 -4
- package/dist/commands/knowledges/index.js.map +1 -1
- package/dist/commands/knowledges/list.js +6 -6
- package/dist/commands/knowledges/read.js +6 -6
- package/dist/commands/knowledges/search.js +6 -6
- package/dist/commands/knowledges/status.js +6 -6
- package/dist/commands/login.js +5 -5
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/scrub.js +7 -7
- package/dist/commands/scrub.js.map +1 -1
- package/dist/commands/transcripts.js +9 -9
- package/dist/commands/transcripts.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HUZZPV5E.js.map +0 -1
- package/dist/chunk-TZKXST4G.js.map +0 -1
- package/dist/chunk-XDXRVTPK.js +0 -18
- package/dist/chunk-XDXRVTPK.js.map +0 -1
- package/dist/chunk-Z4DDLEWR.js.map +0 -1
- /package/dist/{chunk-QEZQGKFX.js.map → chunk-PXSVLAAG.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,130 +1,297 @@
|
|
|
1
1
|
# cngkit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/cngkit)
|
|
4
|
+
[](https://nodejs.org/)
|
|
5
|
+
|
|
6
|
+
Curly.ng's operator CLI for shared code rooms, safe local cleanup, local agent transcript
|
|
7
|
+
inspection, and terminal access to the hosted Harness knowledge catalog.
|
|
8
|
+
|
|
9
|
+
`cngkit` is built for developers and AI agents who need practical commands that work
|
|
10
|
+
well in terminals, scripts, and coding assistants. Human-facing status lines use Ink
|
|
11
|
+
styling when the terminal supports color. Data-heavy output stays plain and easy to pipe.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
Run it without installing:
|
|
4
16
|
|
|
5
17
|
```bash
|
|
6
|
-
npx cngkit
|
|
18
|
+
npx --yes cngkit@latest --help
|
|
7
19
|
```
|
|
8
20
|
|
|
9
|
-
|
|
10
|
-
the Cloudflare-backed Harness knowledges catalog:
|
|
21
|
+
Or install it globally:
|
|
11
22
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- `cngkit scrub [path] --yes` rewrites detected secret values inline with `[CNGKIT_SECRET:<detector>:<verified|unverified>]` placeholders.
|
|
17
|
-
- `cngkit transcripts list [--source all|codex|claude] [--limit n]` lists recent local Claude/Codex transcript files.
|
|
18
|
-
- `cngkit transcripts read <path-or-session-id> [--limit n]` prints normalized user/assistant transcript entries.
|
|
19
|
-
- `cngkit transcripts grep <query> [--limit n] [--file-limit n]` searches recent local transcript entries.
|
|
20
|
-
- `cngkit knowledges status` prints the remote catalog state.
|
|
21
|
-
- `cngkit knowledges audiences` lists available audience filters.
|
|
22
|
-
- `cngkit knowledges search <query> [--limit n]` runs semantic search against Cloudflare Vectorize (default limit 5).
|
|
23
|
-
- `cngkit knowledges list [query] [--limit n]` lists known subskills (default limit 25).
|
|
24
|
-
- `cngkit knowledges files [query] [--audience id] [--limit n]` lists uploaded catalog files (default limit 25).
|
|
25
|
-
- `cngkit knowledges read <file-path> [--offset n] [--limit n]` reads a catalog file excerpt (default limit 200).
|
|
26
|
-
- `cngkit knowledges grep <pattern> [--path path] [--include glob] [--output-mode content|files_with_matches|count] [--context n] [--case-insensitive]` searches catalog file contents (default mode `content`, default path `/`).
|
|
27
|
-
- `cngkit knowledges glob [pattern] [--path path]` lists catalog files by supported glob pattern (default pattern `**/*.md`, default path `/`).
|
|
28
|
-
- `.git/` and files ignored by the repo's `.gitignore` are not synced.
|
|
29
|
-
- Later changes override earlier changes for the MVP conflict rule.
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g cngkit
|
|
25
|
+
cngkit --help
|
|
26
|
+
```
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
Check the installed version:
|
|
32
29
|
|
|
33
30
|
```bash
|
|
34
|
-
cngkit --
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
cngkit
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
cngkit --version
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`cngkit` requires Node.js 20 or newer.
|
|
35
|
+
|
|
36
|
+
## What It Does
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
cngkit login
|
|
40
|
+
cngkit coderoom share
|
|
41
|
+
cngkit coderoom join <room-code>
|
|
42
|
+
cngkit scrub [path]
|
|
43
|
+
cngkit transcripts list --limit 12
|
|
44
|
+
cngkit knowledges search "cloudflare backend" --limit 3
|
|
45
|
+
cngkit knowledges read /libraries/lib-cloudflare/SUBSKILL.md --limit 80
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The CLI currently has four main jobs:
|
|
49
|
+
|
|
50
|
+
- **Coderoom**: start or join a live shared working-tree room.
|
|
51
|
+
- **Scrub**: scan local files for secrets and optionally mask them inline.
|
|
52
|
+
- **Transcripts**: inspect local Claude and Codex transcript files from the terminal.
|
|
53
|
+
- **Knowledges**: search and read the hosted Harness knowledge catalog.
|
|
54
|
+
|
|
55
|
+
## Coderoom
|
|
56
|
+
|
|
57
|
+
Coderoom is a live room for quickly sharing a working tree with another developer or agent.
|
|
58
|
+
One machine starts a room, another joins with the room code, and the Curly backend relays
|
|
59
|
+
file changes.
|
|
60
|
+
|
|
61
|
+
Start a room:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cngkit coderoom share
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Join a room:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cngkit coderoom join <room-code>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Use your own room code:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cngkit coderoom share design-review-01
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Coderoom deliberately skips `.git/` and files ignored by `.gitignore`. It is a live relay,
|
|
80
|
+
not durable cloud storage or a backup system. The current conflict rule is simple: the latest
|
|
81
|
+
received change wins.
|
|
82
|
+
|
|
83
|
+
## Harness Knowledges
|
|
84
|
+
|
|
85
|
+
The `knowledges` commands read the Cloudflare-backed Harness catalog from Curly.ng. They are
|
|
86
|
+
read-only and designed for AI-friendly terminal use.
|
|
87
|
+
|
|
88
|
+
Check catalog health:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
cngkit knowledges status
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Find relevant knowledge:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
cngkit knowledges search "cloudflare backend" --limit 3
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Read a catalog file:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
cngkit knowledges read /libraries/lib-cloudflare/SUBSKILL.md --limit 80
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
List matching files:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
cngkit knowledges glob "**/*.md" --path /libraries/lib-cloudflare
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Search inside files:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
cngkit knowledges grep Cloudflare --path /libraries/lib-cloudflare --output-mode files_with_matches
|
|
41
116
|
```
|
|
42
117
|
|
|
43
|
-
|
|
118
|
+
Return JSON for another tool:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
cngkit knowledges status --json
|
|
122
|
+
cngkit knowledges search "vector search" --limit 5 --json
|
|
123
|
+
```
|
|
44
124
|
|
|
45
|
-
|
|
46
|
-
- `--version` prints the installed `cngkit` version.
|
|
47
|
-
- `--help` / `-h` prints the progressive help for the current command.
|
|
125
|
+
Catalog path shortcuts are supported. For example:
|
|
48
126
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
127
|
+
```text
|
|
128
|
+
/libraries/lib-cloudflare/SUBSKILL.md
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
maps to:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
skills/knowledges/subskills/libraries/lib-cloudflare/SUBSKILL.md
|
|
135
|
+
```
|
|
52
136
|
|
|
53
|
-
|
|
54
|
-
through the Ink runner in `src/cli/command-runner.tsx`. The route files are interface
|
|
55
|
-
adapters only. Command behavior lives under `src/features/<feature>/`, and shared
|
|
56
|
-
runtime concerns such as config, output, browser opening, and API client construction
|
|
57
|
-
live under `src/shared/`. `tsup` emits a file-preserving ESM build so the published
|
|
58
|
-
package contains `dist/cli.js` plus `dist/commands/**`; the private workspace client
|
|
59
|
-
stays bundled while public runtime packages remain normal package dependencies.
|
|
137
|
+
## Secret Scrubbing
|
|
60
138
|
|
|
61
|
-
`scrub`
|
|
139
|
+
`scrub` scans a file or directory with TruffleHog and prints a redacted report.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
cngkit scrub .
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Inline masking rewrites files, so it is gated behind `--yes`:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
cngkit scrub . --yes
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Masked values use this format:
|
|
152
|
+
|
|
153
|
+
```text
|
|
154
|
+
[CNGKIT_SECRET:<detector>:<verified|unverified>]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`scrub` requires the `trufflehog` binary on `PATH`.
|
|
158
|
+
|
|
159
|
+
macOS install:
|
|
62
160
|
|
|
63
161
|
```bash
|
|
64
162
|
brew install trufflehog
|
|
65
163
|
```
|
|
66
164
|
|
|
67
|
-
|
|
165
|
+
Safety notes:
|
|
166
|
+
|
|
167
|
+
- The default mode is report-only.
|
|
168
|
+
- Raw secret values are never printed.
|
|
169
|
+
- `--mask` is accepted as a compatibility alias, but inline changes still require `--yes`.
|
|
68
170
|
|
|
69
171
|
## Local Agent Transcripts
|
|
70
172
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
173
|
+
`transcripts` reads local JSONL files from:
|
|
174
|
+
|
|
175
|
+
- `~/.codex/sessions`
|
|
176
|
+
- `~/.codex/archived_sessions`
|
|
177
|
+
- `~/.claude/projects`
|
|
178
|
+
- `~/.claude/history.jsonl`
|
|
179
|
+
|
|
180
|
+
List recent transcript files:
|
|
74
181
|
|
|
75
182
|
```bash
|
|
76
183
|
cngkit transcripts list --limit 12
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Read a transcript by path or partial session id:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
77
189
|
cngkit transcripts read <path-or-session-id> --source codex --limit 80
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Search recent transcript entries:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
78
195
|
cngkit transcripts grep "deploy failed" --source all --file-limit 60 --limit 20
|
|
79
196
|
```
|
|
80
197
|
|
|
81
|
-
|
|
82
|
-
|
|
198
|
+
Transcript commands are local-only. They do not upload transcript content to Curly.ng.
|
|
199
|
+
By default, they print user and assistant text and skip internal prompt, hook, and tool noise.
|
|
200
|
+
Use `--include-internal` when debugging transcript plumbing.
|
|
83
201
|
|
|
84
|
-
##
|
|
202
|
+
## Backend Selection
|
|
85
203
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
204
|
+
By default, `cngkit` talks to:
|
|
205
|
+
|
|
206
|
+
```text
|
|
207
|
+
https://curly.ng
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Override the backend for one command:
|
|
89
211
|
|
|
90
212
|
```bash
|
|
91
|
-
cngkit
|
|
92
|
-
cngkit coderoom join <room-code>
|
|
213
|
+
cngkit --api-base-url https://curly.ng knowledges status
|
|
93
214
|
```
|
|
94
215
|
|
|
95
|
-
|
|
96
|
-
shared workspace: a room code acts like a share link, connected machines act like
|
|
97
|
-
collaborators, and file events act like the activity stream. Current Coderoom
|
|
98
|
-
rooms are live relays, not durable cloud storage.
|
|
216
|
+
Or with an environment variable:
|
|
99
217
|
|
|
100
|
-
|
|
218
|
+
```bash
|
|
219
|
+
CNGKIT_API_BASE_URL=https://curly.ng cngkit knowledges status
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Help
|
|
101
223
|
|
|
102
|
-
|
|
103
|
-
Curly API. These commands are read-only:
|
|
224
|
+
Help is baked into the CLI as Markdown:
|
|
104
225
|
|
|
105
226
|
```bash
|
|
106
|
-
cngkit
|
|
107
|
-
cngkit
|
|
108
|
-
cngkit knowledges
|
|
109
|
-
cngkit
|
|
110
|
-
cngkit knowledges read
|
|
111
|
-
cngkit
|
|
112
|
-
cngkit knowledges glob "**/*.md" --path /libraries/lib-cloudflare
|
|
227
|
+
cngkit --help
|
|
228
|
+
cngkit help
|
|
229
|
+
cngkit help knowledges
|
|
230
|
+
cngkit coderoom --help
|
|
231
|
+
cngkit knowledges read --help
|
|
232
|
+
cngkit transcripts --help
|
|
113
233
|
```
|
|
114
234
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`--case-insensitive`. Result limits default to `5` for `search` and `25` for `list` and
|
|
119
|
-
`files`; `read` defaults to `200` lines with a backend cap of `2000`, and accepts `--offset`
|
|
120
|
-
for paging.
|
|
235
|
+
Help and machine-readable output are intentionally plain text. Status, warnings, and errors
|
|
236
|
+
use Ink color in interactive terminals, but `--json`, file reads, glob results, grep results,
|
|
237
|
+
and piped output stay clean.
|
|
121
238
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
239
|
+
## Terminal UI Stack
|
|
240
|
+
|
|
241
|
+
`cngkit` uses the Ink family without adding a custom command framework:
|
|
242
|
+
|
|
243
|
+
- **Pastel** owns command and subcommand routing through files under `src/commands/`.
|
|
244
|
+
- **Ink** owns terminal rendering through built-in components such as `<Text>`.
|
|
245
|
+
- **Zod** owns option and argument schemas for each route.
|
|
246
|
+
|
|
247
|
+
So yes, subcommands are built in through Pastel's nested route files. Adding a
|
|
248
|
+
subcommand is normally a new `.tsx` file under the matching command folder:
|
|
249
|
+
|
|
250
|
+
```text
|
|
251
|
+
src/commands/
|
|
252
|
+
coderoom/
|
|
253
|
+
share.tsx cngkit coderoom share
|
|
254
|
+
join.tsx cngkit coderoom join
|
|
255
|
+
knowledges/
|
|
256
|
+
read.tsx cngkit knowledges read
|
|
257
|
+
grep.tsx cngkit knowledges grep
|
|
258
|
+
glob.tsx cngkit knowledges glob
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The CLI does not hand-roll ANSI escape codes. Status styles go through Ink's built-in
|
|
262
|
+
`<Text color="..." bold dimColor>` support. Commands that return content for agents or shell
|
|
263
|
+
pipelines write raw lines instead.
|
|
264
|
+
|
|
265
|
+
## For Contributors
|
|
266
|
+
|
|
267
|
+
The package source lives in `apps/cng`.
|
|
268
|
+
|
|
269
|
+
```text
|
|
270
|
+
src/
|
|
271
|
+
cli/ Pastel bootstrap support, help text, option schemas, Ink output runner
|
|
272
|
+
commands/ Thin Pastel route files
|
|
273
|
+
features/ Command behavior grouped by feature
|
|
274
|
+
shared/ Config, output, browser, API client, shared command utilities
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The route files under `src/commands/` should stay thin. Put behavior in
|
|
278
|
+
`src/features/<feature>/`, and put cross-feature runtime helpers in `src/shared/`.
|
|
279
|
+
For output, use the shared `CommandOutput` methods:
|
|
280
|
+
|
|
281
|
+
- `success`, `warning`, `info`, `muted`, and `error` for human-facing terminal status.
|
|
282
|
+
- `raw` for JSON, file content, file paths, grep results, and anything another tool may parse.
|
|
283
|
+
|
|
284
|
+
Useful local checks:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
pnpm --filter cngkit run typecheck
|
|
288
|
+
pnpm --filter cngkit run lint
|
|
289
|
+
pnpm --filter cngkit run build
|
|
290
|
+
pnpm --filter cngkit run smoke
|
|
291
|
+
```
|
|
125
292
|
|
|
126
|
-
|
|
127
|
-
|
|
293
|
+
The package build uses `tsup` to emit a file-preserving ESM build. The published binary is
|
|
294
|
+
`dist/cli.js`, and Pastel discovers command files under `dist/commands/**`.
|
|
128
295
|
|
|
129
296
|
## License
|
|
130
297
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createCommandOutput,
|
|
2
3
|
formatError
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-X3THKPHT.js";
|
|
4
5
|
|
|
5
6
|
// src/cli/command-runner.tsx
|
|
6
7
|
import { useEffect } from "react";
|
|
@@ -8,16 +9,7 @@ import { useApp } from "ink";
|
|
|
8
9
|
function CommandRunner({ run }) {
|
|
9
10
|
const { exit } = useApp();
|
|
10
11
|
useEffect(() => {
|
|
11
|
-
const output =
|
|
12
|
-
info(message) {
|
|
13
|
-
process.stdout.write(`${message}
|
|
14
|
-
`);
|
|
15
|
-
},
|
|
16
|
-
error(message) {
|
|
17
|
-
process.stderr.write(`${message}
|
|
18
|
-
`);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
12
|
+
const output = createCommandOutput(process.stdout, process.stderr);
|
|
21
13
|
void run(output).catch((error) => {
|
|
22
14
|
process.exitCode = 1;
|
|
23
15
|
output.error(formatError(error));
|
|
@@ -31,4 +23,4 @@ function CommandRunner({ run }) {
|
|
|
31
23
|
export {
|
|
32
24
|
CommandRunner
|
|
33
25
|
};
|
|
34
|
-
//# sourceMappingURL=chunk-
|
|
26
|
+
//# sourceMappingURL=chunk-CELXWAUR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/command-runner.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport { useApp } from \"ink\";\n\nimport { createCommandOutput, formatError, type CommandOutput } 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\n useEffect(() => {\n const output = createCommandOutput(process.stdout, process.stderr);\n\n void run(output)\n .catch((error: unknown) => {\n process.exitCode = 1;\n output.error(formatError(error));\n })\n .finally(() => {\n exit();\n });\n }, [exit, run]);\n\n return null;\n}\n"],"mappings":";;;;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AAQhB,SAAS,cAAc,EAAE,IAAI,GAAuB;AACzD,QAAM,EAAE,KAAK,IAAI,OAAO;AAExB,YAAU,MAAM;AACd,UAAM,SAAS,oBAAoB,QAAQ,QAAQ,QAAQ,MAAM;AAEjE,SAAK,IAAI,MAAM,EACZ,MAAM,CAAC,UAAmB;AACzB,cAAQ,WAAW;AACnB,aAAO,MAAM,YAAY,KAAK,CAAC;AAAA,IACjC,CAAC,EACA,QAAQ,MAAM;AACb,WAAK;AAAA,IACP,CAAC;AAAA,EACL,GAAG,CAAC,MAAM,GAAG,CAAC;AAEd,SAAO;AACT;","names":[]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readBackendHealth
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PXSVLAAG.js";
|
|
4
4
|
import {
|
|
5
5
|
createPeerId,
|
|
6
6
|
createRoomCode,
|
|
7
7
|
resolveApiBaseUrl
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WR4HJ57I.js";
|
|
9
9
|
|
|
10
10
|
// src/features/coderoom/run-coderoom-command.ts
|
|
11
11
|
import process2 from "process";
|
|
@@ -227,9 +227,9 @@ async function startSyncSession(options) {
|
|
|
227
227
|
const suppressionTracker = createSuppressionTracker();
|
|
228
228
|
const webSocketUrl = createSyncWebSocketUrl(options.apiBaseUrl, options.roomCode);
|
|
229
229
|
const socket = new WebSocket(webSocketUrl);
|
|
230
|
-
options.output.
|
|
230
|
+
options.output.success(`Room: ${options.roomCode}`);
|
|
231
231
|
options.output.info(`Repo: ${repoContext.rootDir}`);
|
|
232
|
-
options.output.
|
|
232
|
+
options.output.muted(`Peer: ${peerId}`);
|
|
233
233
|
const watcherContext = {
|
|
234
234
|
repoContext,
|
|
235
235
|
socket,
|
|
@@ -289,7 +289,7 @@ async function sendInitialSnapshot(socket, repoContext, peerId, output) {
|
|
|
289
289
|
sentAt: Date.now(),
|
|
290
290
|
fileCount
|
|
291
291
|
});
|
|
292
|
-
output.
|
|
292
|
+
output.success(`sent snapshot ${fileCount} files`);
|
|
293
293
|
}
|
|
294
294
|
async function sendLocalFileChange(context, absolutePath) {
|
|
295
295
|
const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);
|
|
@@ -301,7 +301,7 @@ async function sendLocalFileChange(context, absolutePath) {
|
|
|
301
301
|
return;
|
|
302
302
|
}
|
|
303
303
|
sendMessage(context.socket, message);
|
|
304
|
-
context.output.
|
|
304
|
+
context.output.muted(`sent file ${relativePath}`);
|
|
305
305
|
}
|
|
306
306
|
async function sendLocalDelete(context, absolutePath) {
|
|
307
307
|
const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);
|
|
@@ -318,7 +318,7 @@ async function sendLocalDelete(context, absolutePath) {
|
|
|
318
318
|
mtimeMs: Date.now(),
|
|
319
319
|
sentAt: Date.now()
|
|
320
320
|
});
|
|
321
|
-
context.output.
|
|
321
|
+
context.output.warning(`sent delete ${relativePath}`);
|
|
322
322
|
}
|
|
323
323
|
async function handleRemoteMessage(options) {
|
|
324
324
|
const decodedMessage = decodeSyncMessage(options.data.toString());
|
|
@@ -330,11 +330,11 @@ async function handleRemoteMessage(options) {
|
|
|
330
330
|
return;
|
|
331
331
|
}
|
|
332
332
|
if (decodedMessage.type === "snapshot-complete") {
|
|
333
|
-
options.output.
|
|
333
|
+
options.output.success(`peer snapshot complete ${decodedMessage.fileCount} files`);
|
|
334
334
|
return;
|
|
335
335
|
}
|
|
336
336
|
await applyRemoteMessage(options.repoContext, decodedMessage, options.suppressionTracker);
|
|
337
|
-
options.output.
|
|
337
|
+
options.output.success(`applied ${decodedMessage.type} ${decodedMessage.path}`);
|
|
338
338
|
}
|
|
339
339
|
async function waitForSessionClose(socket, watcher) {
|
|
340
340
|
await new Promise((resolve, reject) => {
|
|
@@ -379,7 +379,7 @@ async function waitForSessionClose(socket, watcher) {
|
|
|
379
379
|
// src/features/coderoom/run-coderoom-command.ts
|
|
380
380
|
async function runShareCommand(roomCode, options, output) {
|
|
381
381
|
const syncRoomCode = roomCode ?? createRoomCode();
|
|
382
|
-
output.
|
|
382
|
+
output.success(`Share code: ${syncRoomCode}`);
|
|
383
383
|
await printBackendStatus(options, output);
|
|
384
384
|
await runSyncSession("share", syncRoomCode, options, output);
|
|
385
385
|
}
|
|
@@ -392,7 +392,11 @@ async function runJoinCommand(roomCode, options, output) {
|
|
|
392
392
|
}
|
|
393
393
|
async function printBackendStatus(options, output) {
|
|
394
394
|
const health = await readBackendHealth(options);
|
|
395
|
-
|
|
395
|
+
if (health.ok) {
|
|
396
|
+
output.success(`API: ${health.service} ready`);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
output.warning(`API: unavailable (${health.message})`);
|
|
396
400
|
}
|
|
397
401
|
async function runSyncSession(role, roomCode, options, output) {
|
|
398
402
|
await startSyncSession({
|
|
@@ -408,4 +412,4 @@ export {
|
|
|
408
412
|
runShareCommand,
|
|
409
413
|
runJoinCommand
|
|
410
414
|
};
|
|
411
|
-
//# sourceMappingURL=chunk-
|
|
415
|
+
//# sourceMappingURL=chunk-FIRWCD2M.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\";\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 return relativePath?.split(\"/\").includes(\".git\") ?? false;\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\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","path","path","process"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveApiBaseUrl
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WR4HJ57I.js";
|
|
4
4
|
import {
|
|
5
5
|
__export
|
|
6
6
|
} from "./chunk-PZ5AY32C.js";
|
|
@@ -3231,4 +3231,4 @@ export {
|
|
|
3231
3231
|
createCngApiClient,
|
|
3232
3232
|
readBackendHealth
|
|
3233
3233
|
};
|
|
3234
|
-
//# sourceMappingURL=chunk-
|
|
3234
|
+
//# sourceMappingURL=chunk-PXSVLAAG.js.map
|