cngkit 1.1.5 → 1.1.7
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 +7 -4
- package/dist/{chunk-TZRXQ6GR.js → chunk-EQEIX7N5.js} +3 -3
- package/dist/chunk-EQEIX7N5.js.map +1 -0
- package/dist/chunk-HUZZPV5E.js +411 -0
- package/dist/chunk-HUZZPV5E.js.map +1 -0
- package/dist/{chunk-YY2VGJ5N.js → chunk-MLKBG5YJ.js} +2 -2
- package/dist/{chunk-YY2VGJ5N.js.map → chunk-MLKBG5YJ.js.map} +1 -1
- package/dist/{chunk-UXMP5Z5P.js → chunk-QEZQGKFX.js} +13 -1364
- package/dist/{chunk-UXMP5Z5P.js.map → chunk-QEZQGKFX.js.map} +1 -1
- package/dist/chunk-QZEB4VMX.js +32 -0
- package/dist/chunk-QZEB4VMX.js.map +1 -0
- package/dist/chunk-TZKXST4G.js +291 -0
- package/dist/chunk-TZKXST4G.js.map +1 -0
- package/dist/{chunk-SSRUN6G5.js → chunk-VI5XQH3U.js} +3 -18
- package/dist/chunk-VI5XQH3U.js.map +1 -0
- package/dist/chunk-XDXRVTPK.js +18 -0
- package/dist/chunk-XDXRVTPK.js.map +1 -0
- package/dist/{chunk-SNTLRTQ2.js → chunk-Z4DDLEWR.js} +3 -3
- package/dist/chunk-Z4DDLEWR.js.map +1 -0
- package/dist/cli.js +8 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/coderoom/index.js +6 -5
- package/dist/commands/coderoom/index.js.map +1 -1
- package/dist/commands/coderoom/join.js +6 -5
- package/dist/commands/coderoom/join.js.map +1 -1
- package/dist/commands/coderoom/share.js +6 -5
- package/dist/commands/coderoom/share.js.map +1 -1
- package/dist/commands/index.js +5 -4
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/knowledges/audiences.js +8 -5
- package/dist/commands/knowledges/audiences.js.map +1 -1
- package/dist/commands/knowledges/files.js +8 -5
- package/dist/commands/knowledges/files.js.map +1 -1
- package/dist/commands/knowledges/glob.js +8 -5
- package/dist/commands/knowledges/glob.js.map +1 -1
- package/dist/commands/knowledges/grep.js +8 -5
- package/dist/commands/knowledges/grep.js.map +1 -1
- package/dist/commands/knowledges/index.js +6 -5
- package/dist/commands/knowledges/index.js.map +1 -1
- package/dist/commands/knowledges/list.js +8 -5
- package/dist/commands/knowledges/list.js.map +1 -1
- package/dist/commands/knowledges/read.js +8 -5
- package/dist/commands/knowledges/read.js.map +1 -1
- package/dist/commands/knowledges/search.js +8 -5
- package/dist/commands/knowledges/search.js.map +1 -1
- package/dist/commands/knowledges/status.js +8 -5
- package/dist/commands/knowledges/status.js.map +1 -1
- package/dist/commands/login.js +49 -6
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/scrub.js +256 -7
- package/dist/commands/scrub.js.map +1 -1
- package/dist/commands/transcripts.js +377 -6
- package/dist/commands/transcripts.js.map +1 -1
- package/package.json +15 -14
- package/dist/chunk-SNTLRTQ2.js.map +0 -1
- package/dist/chunk-SSRUN6G5.js.map +0 -1
- package/dist/chunk-TZRXQ6GR.js.map +0 -1
package/README.md
CHANGED
|
@@ -47,13 +47,16 @@ Global options accepted by every command:
|
|
|
47
47
|
- `--help` / `-h` prints the progressive help for the current command.
|
|
48
48
|
|
|
49
49
|
The canonical CLI spec lives at `docs/superpowers/specs/2026-06-25-cngkit-cli-spec.md`
|
|
50
|
-
in the repository. The baked help source lives in `src/help-specs.ts`; keep both
|
|
50
|
+
in the repository. The baked help source lives in `src/cli/help-specs.ts`; keep both
|
|
51
51
|
aligned when changing command behavior.
|
|
52
52
|
|
|
53
53
|
Command routing is implemented with Pastel route files under `src/commands/`, rendered
|
|
54
|
-
through Ink
|
|
55
|
-
|
|
56
|
-
|
|
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.
|
|
57
60
|
|
|
58
61
|
`scrub` requires the `trufflehog` binary on `PATH`. On macOS, install it with:
|
|
59
62
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// src/config.ts
|
|
1
|
+
// src/shared/config.ts
|
|
2
2
|
import { randomBytes, randomUUID } from "crypto";
|
|
3
3
|
import process from "process";
|
|
4
|
-
var packageVersion = "1.1.
|
|
4
|
+
var packageVersion = "1.1.7";
|
|
5
5
|
var defaultApiBaseUrl = "https://curly.ng";
|
|
6
6
|
function resolveApiBaseUrl(options) {
|
|
7
7
|
return options.apiBaseUrl ?? process.env.CNGKIT_API_BASE_URL ?? defaultApiBaseUrl;
|
|
@@ -19,4 +19,4 @@ export {
|
|
|
19
19
|
createRoomCode,
|
|
20
20
|
createPeerId
|
|
21
21
|
};
|
|
22
|
-
//# sourceMappingURL=chunk-
|
|
22
|
+
//# sourceMappingURL=chunk-EQEIX7N5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/config.ts"],"sourcesContent":["import { randomBytes, randomUUID } from \"node:crypto\";\nimport process from \"node:process\";\n\nexport const packageVersion = \"1.1.7\";\nexport const defaultApiBaseUrl = \"https://curly.ng\";\n\nexport type GlobalCommandOptions = {\n apiBaseUrl?: string;\n};\n\nexport function resolveApiBaseUrl(options: GlobalCommandOptions): string {\n return options.apiBaseUrl ?? process.env.CNGKIT_API_BASE_URL ?? defaultApiBaseUrl;\n}\n\nexport function createRoomCode(): string {\n return randomBytes(4).toString(\"hex\").toUpperCase();\n}\n\nexport function createPeerId(): string {\n return randomUUID();\n}\n"],"mappings":";AAAA,SAAS,aAAa,kBAAkB;AACxC,OAAO,aAAa;AAEb,IAAM,iBAAiB;AACvB,IAAM,oBAAoB;AAM1B,SAAS,kBAAkB,SAAuC;AACvE,SAAO,QAAQ,cAAc,QAAQ,IAAI,uBAAuB;AAClE;AAEO,SAAS,iBAAyB;AACvC,SAAO,YAAY,CAAC,EAAE,SAAS,KAAK,EAAE,YAAY;AACpD;AAEO,SAAS,eAAuB;AACrC,SAAO,WAAW;AACpB;","names":[]}
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readBackendHealth
|
|
3
|
+
} from "./chunk-QEZQGKFX.js";
|
|
4
|
+
import {
|
|
5
|
+
createPeerId,
|
|
6
|
+
createRoomCode,
|
|
7
|
+
resolveApiBaseUrl
|
|
8
|
+
} from "./chunk-EQEIX7N5.js";
|
|
9
|
+
|
|
10
|
+
// src/features/coderoom/run-coderoom-command.ts
|
|
11
|
+
import process2 from "process";
|
|
12
|
+
|
|
13
|
+
// src/features/coderoom/sync/client.ts
|
|
14
|
+
import process from "process";
|
|
15
|
+
import chokidar from "chokidar";
|
|
16
|
+
import WebSocket from "ws";
|
|
17
|
+
|
|
18
|
+
// src/features/coderoom/sync/files.ts
|
|
19
|
+
import fs from "fs/promises";
|
|
20
|
+
import path2 from "path";
|
|
21
|
+
|
|
22
|
+
// src/features/coderoom/sync/paths.ts
|
|
23
|
+
import { execFile } from "child_process";
|
|
24
|
+
import path from "path";
|
|
25
|
+
import { promisify } from "util";
|
|
26
|
+
var execFileAsync = promisify(execFile);
|
|
27
|
+
async function resolveRepoContext(cwd) {
|
|
28
|
+
try {
|
|
29
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
|
|
30
|
+
cwd
|
|
31
|
+
});
|
|
32
|
+
return { rootDir: path.resolve(stdout.trim()) };
|
|
33
|
+
} catch {
|
|
34
|
+
return { rootDir: path.resolve(cwd) };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function toRepoRelativePath(rootDir, absolutePath) {
|
|
38
|
+
const relativePath = path.relative(rootDir, absolutePath);
|
|
39
|
+
if (!relativePath || relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
return relativePath.split(path.sep).join("/");
|
|
43
|
+
}
|
|
44
|
+
function resolveRepoPath(rootDir, relativePath) {
|
|
45
|
+
if (!isSafeRelativePath(relativePath)) {
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
const absolutePath = path.resolve(rootDir, relativePath);
|
|
49
|
+
const normalizedRoot = `${path.resolve(rootDir)}${path.sep}`;
|
|
50
|
+
if (absolutePath !== path.resolve(rootDir) && !absolutePath.startsWith(normalizedRoot)) {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
return absolutePath;
|
|
54
|
+
}
|
|
55
|
+
function isSafeRelativePath(relativePath) {
|
|
56
|
+
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\0")) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const normalizedParts = relativePath.split(/[\\/]+/).filter(Boolean);
|
|
60
|
+
if (normalizedParts.includes("..")) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return normalizedParts[0] !== ".git";
|
|
64
|
+
}
|
|
65
|
+
async function shouldSyncRelativePath(context, relativePath) {
|
|
66
|
+
if (!isSafeRelativePath(relativePath)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
await execFileAsync("git", ["check-ignore", "--quiet", "--", relativePath], {
|
|
71
|
+
cwd: context.rootDir
|
|
72
|
+
});
|
|
73
|
+
return false;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
const exitCode = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
76
|
+
return exitCode === 1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/features/coderoom/sync/files.ts
|
|
81
|
+
function createSuppressionTracker(windowMs = 1500) {
|
|
82
|
+
const suppressedUntilByPath = /* @__PURE__ */ new Map();
|
|
83
|
+
return {
|
|
84
|
+
suppress(relativePath) {
|
|
85
|
+
suppressedUntilByPath.set(relativePath, Date.now() + windowMs);
|
|
86
|
+
},
|
|
87
|
+
isSuppressed(relativePath) {
|
|
88
|
+
const suppressedUntil = suppressedUntilByPath.get(relativePath);
|
|
89
|
+
if (!suppressedUntil) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (suppressedUntil < Date.now()) {
|
|
93
|
+
suppressedUntilByPath.delete(relativePath);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function* collectSnapshotMessages(context, peerId) {
|
|
101
|
+
yield* collectDirectorySnapshot(context, context.rootDir, peerId);
|
|
102
|
+
}
|
|
103
|
+
async function* collectDirectorySnapshot(context, directoryPath, peerId) {
|
|
104
|
+
const entries = await fs.opendir(directoryPath);
|
|
105
|
+
for await (const entry of entries) {
|
|
106
|
+
const absolutePath = path2.join(directoryPath, entry.name);
|
|
107
|
+
const relativePath = toRepoRelativePath(context.rootDir, absolutePath);
|
|
108
|
+
if (!relativePath || !await shouldSyncRelativePath(context, relativePath)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (entry.isDirectory()) {
|
|
112
|
+
yield* collectDirectorySnapshot(context, absolutePath, peerId);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!entry.isFile()) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const [content, stat] = await Promise.all([fs.readFile(absolutePath), fs.stat(absolutePath)]);
|
|
119
|
+
yield {
|
|
120
|
+
type: "file",
|
|
121
|
+
peerId,
|
|
122
|
+
path: relativePath,
|
|
123
|
+
contentBase64: content.toString("base64"),
|
|
124
|
+
mtimeMs: stat.mtimeMs,
|
|
125
|
+
sentAt: Date.now()
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function buildFileMessage(context, peerId, relativePath) {
|
|
130
|
+
if (!await shouldSyncRelativePath(context, relativePath)) {
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
const absolutePath = resolveRepoPath(context.rootDir, relativePath);
|
|
134
|
+
if (!absolutePath) {
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
const stat = await fs.stat(absolutePath);
|
|
138
|
+
if (!stat.isFile()) {
|
|
139
|
+
return void 0;
|
|
140
|
+
}
|
|
141
|
+
const content = await fs.readFile(absolutePath);
|
|
142
|
+
return {
|
|
143
|
+
type: "file",
|
|
144
|
+
peerId,
|
|
145
|
+
path: relativePath,
|
|
146
|
+
contentBase64: content.toString("base64"),
|
|
147
|
+
mtimeMs: stat.mtimeMs,
|
|
148
|
+
sentAt: Date.now()
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async function applyRemoteMessage(context, message, suppressionTracker) {
|
|
152
|
+
const absolutePath = resolveRepoPath(context.rootDir, message.path);
|
|
153
|
+
if (!absolutePath || !await shouldSyncRelativePath(context, message.path)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
suppressionTracker.suppress(message.path);
|
|
157
|
+
if (message.type === "delete") {
|
|
158
|
+
await fs.rm(absolutePath, { force: true });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
await fs.mkdir(path2.dirname(absolutePath), { recursive: true });
|
|
162
|
+
await fs.writeFile(absolutePath, Buffer.from(message.contentBase64, "base64"));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/features/coderoom/sync/protocol.ts
|
|
166
|
+
import { z } from "zod";
|
|
167
|
+
var SyncBaseMessageSchema = z.object({
|
|
168
|
+
peerId: z.string().min(1),
|
|
169
|
+
sentAt: z.number().finite()
|
|
170
|
+
});
|
|
171
|
+
var SyncHelloMessageSchema = SyncBaseMessageSchema.extend({
|
|
172
|
+
type: z.literal("hello"),
|
|
173
|
+
role: z.union([z.literal("share"), z.literal("join")])
|
|
174
|
+
});
|
|
175
|
+
var SyncFileMessageSchema = SyncBaseMessageSchema.extend({
|
|
176
|
+
type: z.literal("file"),
|
|
177
|
+
path: z.string().min(1),
|
|
178
|
+
contentBase64: z.string(),
|
|
179
|
+
mtimeMs: z.number().finite()
|
|
180
|
+
});
|
|
181
|
+
var SyncDeleteMessageSchema = SyncBaseMessageSchema.extend({
|
|
182
|
+
type: z.literal("delete"),
|
|
183
|
+
path: z.string().min(1),
|
|
184
|
+
mtimeMs: z.number().finite()
|
|
185
|
+
});
|
|
186
|
+
var SyncSnapshotCompleteMessageSchema = SyncBaseMessageSchema.extend({
|
|
187
|
+
type: z.literal("snapshot-complete"),
|
|
188
|
+
fileCount: z.number().int().nonnegative()
|
|
189
|
+
});
|
|
190
|
+
var SyncMessageSchema = z.discriminatedUnion("type", [
|
|
191
|
+
SyncHelloMessageSchema,
|
|
192
|
+
SyncFileMessageSchema,
|
|
193
|
+
SyncDeleteMessageSchema,
|
|
194
|
+
SyncSnapshotCompleteMessageSchema
|
|
195
|
+
]);
|
|
196
|
+
function encodeSyncMessage(message) {
|
|
197
|
+
return JSON.stringify(SyncMessageSchema.parse(message));
|
|
198
|
+
}
|
|
199
|
+
function decodeSyncMessage(value) {
|
|
200
|
+
if (typeof value !== "string") {
|
|
201
|
+
return void 0;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
return SyncMessageSchema.parse(JSON.parse(value));
|
|
205
|
+
} catch {
|
|
206
|
+
return void 0;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/features/coderoom/sync/client.ts
|
|
211
|
+
function createSyncWebSocketUrl(apiBaseUrl, roomCode) {
|
|
212
|
+
const url = new URL(apiBaseUrl);
|
|
213
|
+
url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
|
|
214
|
+
url.pathname = `/api/cng/sync/${encodeURIComponent(roomCode)}`;
|
|
215
|
+
url.search = "";
|
|
216
|
+
url.hash = "";
|
|
217
|
+
return url.toString();
|
|
218
|
+
}
|
|
219
|
+
function sendMessage(socket, message) {
|
|
220
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
221
|
+
socket.send(encodeSyncMessage(message));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async function startSyncSession(options) {
|
|
225
|
+
const repoContext = await resolveRepoContext(options.cwd);
|
|
226
|
+
const peerId = createPeerId();
|
|
227
|
+
const suppressionTracker = createSuppressionTracker();
|
|
228
|
+
const webSocketUrl = createSyncWebSocketUrl(options.apiBaseUrl, options.roomCode);
|
|
229
|
+
const socket = new WebSocket(webSocketUrl);
|
|
230
|
+
options.output.info(`Room: ${options.roomCode}`);
|
|
231
|
+
options.output.info(`Repo: ${repoContext.rootDir}`);
|
|
232
|
+
options.output.info(`Peer: ${peerId}`);
|
|
233
|
+
const watcherContext = {
|
|
234
|
+
repoContext,
|
|
235
|
+
socket,
|
|
236
|
+
peerId,
|
|
237
|
+
suppressionTracker,
|
|
238
|
+
output: options.output
|
|
239
|
+
};
|
|
240
|
+
const watcher = createRepoWatcher(watcherContext);
|
|
241
|
+
socket.on("open", () => {
|
|
242
|
+
sendMessage(socket, {
|
|
243
|
+
type: "hello",
|
|
244
|
+
peerId,
|
|
245
|
+
role: options.role,
|
|
246
|
+
sentAt: Date.now()
|
|
247
|
+
});
|
|
248
|
+
void sendInitialSnapshot(socket, repoContext, peerId, options.output);
|
|
249
|
+
});
|
|
250
|
+
socket.on("message", (data) => {
|
|
251
|
+
void handleRemoteMessage({
|
|
252
|
+
data,
|
|
253
|
+
repoContext,
|
|
254
|
+
peerId,
|
|
255
|
+
suppressionTracker,
|
|
256
|
+
output: options.output
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
await waitForSessionClose(socket, watcher);
|
|
260
|
+
}
|
|
261
|
+
function createRepoWatcher(context) {
|
|
262
|
+
const watcher = chokidar.watch(context.repoContext.rootDir, {
|
|
263
|
+
ignoreInitial: true,
|
|
264
|
+
ignored: (candidatePath) => {
|
|
265
|
+
const relativePath = toRepoRelativePath(context.repoContext.rootDir, candidatePath);
|
|
266
|
+
return relativePath?.split("/").includes(".git") ?? false;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
watcher.on("add", (absolutePath) => {
|
|
270
|
+
void sendLocalFileChange(context, absolutePath);
|
|
271
|
+
});
|
|
272
|
+
watcher.on("change", (absolutePath) => {
|
|
273
|
+
void sendLocalFileChange(context, absolutePath);
|
|
274
|
+
});
|
|
275
|
+
watcher.on("unlink", (absolutePath) => {
|
|
276
|
+
void sendLocalDelete(context, absolutePath);
|
|
277
|
+
});
|
|
278
|
+
return watcher;
|
|
279
|
+
}
|
|
280
|
+
async function sendInitialSnapshot(socket, repoContext, peerId, output) {
|
|
281
|
+
let fileCount = 0;
|
|
282
|
+
for await (const message of collectSnapshotMessages(repoContext, peerId)) {
|
|
283
|
+
sendMessage(socket, message);
|
|
284
|
+
fileCount += 1;
|
|
285
|
+
}
|
|
286
|
+
sendMessage(socket, {
|
|
287
|
+
type: "snapshot-complete",
|
|
288
|
+
peerId,
|
|
289
|
+
sentAt: Date.now(),
|
|
290
|
+
fileCount
|
|
291
|
+
});
|
|
292
|
+
output.info(`sent snapshot ${fileCount} files`);
|
|
293
|
+
}
|
|
294
|
+
async function sendLocalFileChange(context, absolutePath) {
|
|
295
|
+
const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);
|
|
296
|
+
if (!relativePath || context.suppressionTracker.isSuppressed(relativePath)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const message = await buildFileMessage(context.repoContext, context.peerId, relativePath);
|
|
300
|
+
if (!message) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
sendMessage(context.socket, message);
|
|
304
|
+
context.output.info(`sent file ${relativePath}`);
|
|
305
|
+
}
|
|
306
|
+
async function sendLocalDelete(context, absolutePath) {
|
|
307
|
+
const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);
|
|
308
|
+
if (!relativePath || context.suppressionTracker.isSuppressed(relativePath)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (!await shouldSyncRelativePath(context.repoContext, relativePath)) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
sendMessage(context.socket, {
|
|
315
|
+
type: "delete",
|
|
316
|
+
peerId: context.peerId,
|
|
317
|
+
path: relativePath,
|
|
318
|
+
mtimeMs: Date.now(),
|
|
319
|
+
sentAt: Date.now()
|
|
320
|
+
});
|
|
321
|
+
context.output.info(`sent delete ${relativePath}`);
|
|
322
|
+
}
|
|
323
|
+
async function handleRemoteMessage(options) {
|
|
324
|
+
const decodedMessage = decodeSyncMessage(options.data.toString());
|
|
325
|
+
if (!decodedMessage || decodedMessage.peerId === options.peerId) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (decodedMessage.type === "hello") {
|
|
329
|
+
options.output.info(`peer joined ${decodedMessage.peerId}`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (decodedMessage.type === "snapshot-complete") {
|
|
333
|
+
options.output.info(`peer snapshot complete ${decodedMessage.fileCount} files`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
await applyRemoteMessage(options.repoContext, decodedMessage, options.suppressionTracker);
|
|
337
|
+
options.output.info(`applied ${decodedMessage.type} ${decodedMessage.path}`);
|
|
338
|
+
}
|
|
339
|
+
async function waitForSessionClose(socket, watcher) {
|
|
340
|
+
await new Promise((resolve, reject) => {
|
|
341
|
+
let settled = false;
|
|
342
|
+
const closeSession = async () => {
|
|
343
|
+
await watcher.close();
|
|
344
|
+
if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
|
|
345
|
+
socket.close();
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const finish = async () => {
|
|
349
|
+
if (settled) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
settled = true;
|
|
353
|
+
process.off("SIGINT", onSignal);
|
|
354
|
+
process.off("SIGTERM", onSignal);
|
|
355
|
+
await closeSession();
|
|
356
|
+
resolve();
|
|
357
|
+
};
|
|
358
|
+
const onSignal = () => {
|
|
359
|
+
void finish();
|
|
360
|
+
};
|
|
361
|
+
socket.on("error", async (error) => {
|
|
362
|
+
if (settled) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
settled = true;
|
|
366
|
+
process.off("SIGINT", onSignal);
|
|
367
|
+
process.off("SIGTERM", onSignal);
|
|
368
|
+
await closeSession();
|
|
369
|
+
reject(error);
|
|
370
|
+
});
|
|
371
|
+
socket.on("close", () => {
|
|
372
|
+
void finish();
|
|
373
|
+
});
|
|
374
|
+
process.on("SIGINT", onSignal);
|
|
375
|
+
process.on("SIGTERM", onSignal);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/features/coderoom/run-coderoom-command.ts
|
|
380
|
+
async function runShareCommand(roomCode, options, output) {
|
|
381
|
+
const syncRoomCode = roomCode ?? createRoomCode();
|
|
382
|
+
output.info(`Share code: ${syncRoomCode}`);
|
|
383
|
+
await printBackendStatus(options, output);
|
|
384
|
+
await runSyncSession("share", syncRoomCode, options, output);
|
|
385
|
+
}
|
|
386
|
+
async function runJoinCommand(roomCode, options, output) {
|
|
387
|
+
if (!roomCode) {
|
|
388
|
+
throw new Error("Missing room code. Usage: cngkit coderoom join <room-code>");
|
|
389
|
+
}
|
|
390
|
+
await printBackendStatus(options, output);
|
|
391
|
+
await runSyncSession("join", roomCode, options, output);
|
|
392
|
+
}
|
|
393
|
+
async function printBackendStatus(options, output) {
|
|
394
|
+
const health = await readBackendHealth(options);
|
|
395
|
+
output.info(health.ok ? `API: ${health.service} ready` : `API: unavailable (${health.message})`);
|
|
396
|
+
}
|
|
397
|
+
async function runSyncSession(role, roomCode, options, output) {
|
|
398
|
+
await startSyncSession({
|
|
399
|
+
apiBaseUrl: resolveApiBaseUrl(options),
|
|
400
|
+
roomCode,
|
|
401
|
+
role,
|
|
402
|
+
cwd: process2.cwd(),
|
|
403
|
+
output
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export {
|
|
408
|
+
runShareCommand,
|
|
409
|
+
runJoinCommand
|
|
410
|
+
};
|
|
411
|
+
//# sourceMappingURL=chunk-HUZZPV5E.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.info(`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 output.info(health.ok ? `API: ${health.service} ready` : `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.info(`Room: ${options.roomCode}`);\n options.output.info(`Repo: ${repoContext.rootDir}`);\n options.output.info(`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.info(`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.info(`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.info(`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.info(`peer snapshot complete ${decodedMessage.fileCount} files`);\n return;\n }\n\n await applyRemoteMessage(options.repoContext, decodedMessage, options.suppressionTracker);\n options.output.info(`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,4 +1,4 @@
|
|
|
1
|
-
// src/cli
|
|
1
|
+
// src/cli/options.ts
|
|
2
2
|
import { argument, option } from "pastel";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
var GlobalOptionsSchema = z.object({
|
|
@@ -111,4 +111,4 @@ export {
|
|
|
111
111
|
LimitOptionsSchema,
|
|
112
112
|
TranscriptArgsSchema
|
|
113
113
|
};
|
|
114
|
-
//# sourceMappingURL=chunk-
|
|
114
|
+
//# sourceMappingURL=chunk-MLKBG5YJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli
|
|
1
|
+
{"version":3,"sources":["../src/cli/options.ts"],"sourcesContent":["import { argument, option } from \"pastel\";\nimport { z } from \"zod\";\n\nexport const GlobalOptionsSchema = z.object({\n apiBaseUrl: z\n .string()\n .optional()\n .describe(\n option({\n description: \"Curly API base URL. Default: CNGKIT_API_BASE_URL or https://curly.ng\",\n valueDescription: \"url\",\n })\n ),\n});\n\nexport const JsonOutputOptionsSchema = GlobalOptionsSchema.extend({\n json: z\n .boolean()\n .optional()\n .describe(\n option({\n description: \"Print raw JSON\",\n })\n ),\n});\n\nexport const OptionalPathArgsSchema = z.tuple([\n z\n .string()\n .optional()\n .describe(\n argument({\n name: \"path\",\n description: \"Path to scan\",\n })\n ),\n]);\n\nexport const OptionalRoomCodeArgsSchema = z.tuple([\n z\n .string()\n .optional()\n .describe(\n argument({\n name: \"room-code\",\n description: \"Room code\",\n })\n ),\n]);\n\nexport const RequiredRoomCodeArgsSchema = z.tuple([\n z.string().describe(\n argument({\n name: \"room-code\",\n description: \"Room code\",\n })\n ),\n]);\n\nexport const OptionalQueryArgsSchema = z.tuple([\n z\n .string()\n .optional()\n .describe(\n argument({\n name: \"query\",\n description: \"Optional query\",\n })\n ),\n]);\n\nexport const RequiredQueryArgsSchema = z.array(\n z.string().describe(\n argument({\n name: \"query\",\n description: \"Search query\",\n })\n )\n);\n\nexport const RequiredFilePathArgsSchema = z.tuple([\n z.string().describe(\n argument({\n name: \"file-path\",\n description: \"Catalog file path\",\n })\n ),\n]);\n\nexport const RequiredPatternArgsSchema = z.array(\n z.string().describe(\n argument({\n name: \"pattern\",\n description: \"Search pattern\",\n })\n )\n);\n\nexport const OptionalGlobPatternArgsSchema = z.array(\n z.string().describe(\n argument({\n name: \"pattern\",\n description: \"Glob pattern\",\n })\n )\n);\n\nexport const LimitOptionsSchema = JsonOutputOptionsSchema.extend({\n limit: z\n .number()\n .optional()\n .describe(\n option({\n description: \"Maximum results\",\n valueDescription: \"n\",\n })\n ),\n});\n\nexport const TranscriptArgsSchema = z.array(\n z.string().describe(\n argument({\n name: \"transcript-args\",\n description: \"Transcript action and arguments\",\n })\n )\n);\n"],"mappings":";AAAA,SAAS,UAAU,cAAc;AACjC,SAAS,SAAS;AAEX,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,YAAY,EACT,OAAO,EACP,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,aAAa;AAAA,MACb,kBAAkB;AAAA,IACpB,CAAC;AAAA,EACH;AACJ,CAAC;AAEM,IAAM,0BAA0B,oBAAoB,OAAO;AAAA,EAChE,MAAM,EACH,QAAQ,EACR,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACJ,CAAC;AAEM,IAAM,yBAAyB,EAAE,MAAM;AAAA,EAC5C,EACG,OAAO,EACP,SAAS,EACT;AAAA,IACC,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACJ,CAAC;AAEM,IAAM,6BAA6B,EAAE,MAAM;AAAA,EAChD,EACG,OAAO,EACP,SAAS,EACT;AAAA,IACC,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACJ,CAAC;AAEM,IAAM,6BAA6B,EAAE,MAAM;AAAA,EAChD,EAAE,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACF,CAAC;AAEM,IAAM,0BAA0B,EAAE,MAAM;AAAA,EAC7C,EACG,OAAO,EACP,SAAS,EACT;AAAA,IACC,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACJ,CAAC;AAEM,IAAM,0BAA0B,EAAE;AAAA,EACvC,EAAE,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACF;AAEO,IAAM,6BAA6B,EAAE,MAAM;AAAA,EAChD,EAAE,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACF,CAAC;AAEM,IAAM,4BAA4B,EAAE;AAAA,EACzC,EAAE,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACF;AAEO,IAAM,gCAAgC,EAAE;AAAA,EAC7C,EAAE,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACF;AAEO,IAAM,qBAAqB,wBAAwB,OAAO;AAAA,EAC/D,OAAO,EACJ,OAAO,EACP,SAAS,EACT;AAAA,IACC,OAAO;AAAA,MACL,aAAa;AAAA,MACb,kBAAkB;AAAA,IACpB,CAAC;AAAA,EACH;AACJ,CAAC;AAEM,IAAM,uBAAuB,EAAE;AAAA,EACpC,EAAE,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACF;","names":[]}
|