cngkit 1.1.6 → 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.
Files changed (57) hide show
  1. package/README.md +7 -4
  2. package/dist/{chunk-F6YQGKLK.js → chunk-EQEIX7N5.js} +3 -3
  3. package/dist/chunk-EQEIX7N5.js.map +1 -0
  4. package/dist/chunk-HUZZPV5E.js +411 -0
  5. package/dist/chunk-HUZZPV5E.js.map +1 -0
  6. package/dist/{chunk-YY2VGJ5N.js → chunk-MLKBG5YJ.js} +2 -2
  7. package/dist/{chunk-YY2VGJ5N.js.map → chunk-MLKBG5YJ.js.map} +1 -1
  8. package/dist/{chunk-Z3J7PPZB.js → chunk-QEZQGKFX.js} +13 -1364
  9. package/dist/{chunk-Z3J7PPZB.js.map → chunk-QEZQGKFX.js.map} +1 -1
  10. package/dist/chunk-QZEB4VMX.js +32 -0
  11. package/dist/chunk-QZEB4VMX.js.map +1 -0
  12. package/dist/chunk-TZKXST4G.js +291 -0
  13. package/dist/chunk-TZKXST4G.js.map +1 -0
  14. package/dist/{chunk-SSRUN6G5.js → chunk-VI5XQH3U.js} +3 -18
  15. package/dist/chunk-VI5XQH3U.js.map +1 -0
  16. package/dist/chunk-XDXRVTPK.js +18 -0
  17. package/dist/chunk-XDXRVTPK.js.map +1 -0
  18. package/dist/{chunk-SNTLRTQ2.js → chunk-Z4DDLEWR.js} +3 -3
  19. package/dist/chunk-Z4DDLEWR.js.map +1 -0
  20. package/dist/cli.js +8 -6
  21. package/dist/cli.js.map +1 -1
  22. package/dist/commands/coderoom/index.js +6 -5
  23. package/dist/commands/coderoom/index.js.map +1 -1
  24. package/dist/commands/coderoom/join.js +6 -5
  25. package/dist/commands/coderoom/join.js.map +1 -1
  26. package/dist/commands/coderoom/share.js +6 -5
  27. package/dist/commands/coderoom/share.js.map +1 -1
  28. package/dist/commands/index.js +5 -4
  29. package/dist/commands/index.js.map +1 -1
  30. package/dist/commands/knowledges/audiences.js +8 -5
  31. package/dist/commands/knowledges/audiences.js.map +1 -1
  32. package/dist/commands/knowledges/files.js +8 -5
  33. package/dist/commands/knowledges/files.js.map +1 -1
  34. package/dist/commands/knowledges/glob.js +8 -5
  35. package/dist/commands/knowledges/glob.js.map +1 -1
  36. package/dist/commands/knowledges/grep.js +8 -5
  37. package/dist/commands/knowledges/grep.js.map +1 -1
  38. package/dist/commands/knowledges/index.js +6 -5
  39. package/dist/commands/knowledges/index.js.map +1 -1
  40. package/dist/commands/knowledges/list.js +8 -5
  41. package/dist/commands/knowledges/list.js.map +1 -1
  42. package/dist/commands/knowledges/read.js +8 -5
  43. package/dist/commands/knowledges/read.js.map +1 -1
  44. package/dist/commands/knowledges/search.js +8 -5
  45. package/dist/commands/knowledges/search.js.map +1 -1
  46. package/dist/commands/knowledges/status.js +8 -5
  47. package/dist/commands/knowledges/status.js.map +1 -1
  48. package/dist/commands/login.js +49 -6
  49. package/dist/commands/login.js.map +1 -1
  50. package/dist/commands/scrub.js +256 -7
  51. package/dist/commands/scrub.js.map +1 -1
  52. package/dist/commands/transcripts.js +377 -6
  53. package/dist/commands/transcripts.js.map +1 -1
  54. package/package.json +1 -1
  55. package/dist/chunk-F6YQGKLK.js.map +0 -1
  56. package/dist/chunk-SNTLRTQ2.js.map +0 -1
  57. package/dist/chunk-SSRUN6G5.js.map +0 -1
@@ -1,18 +1,10 @@
1
1
  import {
2
- createPeerId,
3
- createRoomCode,
4
2
  resolveApiBaseUrl
5
- } from "./chunk-F6YQGKLK.js";
6
- import {
7
- formatCngkitHelp
8
- } from "./chunk-SSRUN6G5.js";
3
+ } from "./chunk-EQEIX7N5.js";
9
4
  import {
10
5
  __export
11
6
  } from "./chunk-PZ5AY32C.js";
12
7
 
13
- // src/command-actions.ts
14
- import process4 from "process";
15
-
16
8
  // ../../packages/client/src/generated/core/json.ts
17
9
  var toJson = (value, replacer, space) => {
18
10
  return JSON.stringify(value, replacer, space);
@@ -1821,7 +1813,7 @@ var CngApiEnvironment = {
1821
1813
  };
1822
1814
 
1823
1815
  // ../../packages/client/src/generated/errors/handleNonStatusCodeError.ts
1824
- function handleNonStatusCodeError(error, rawResponse, method, path6) {
1816
+ function handleNonStatusCodeError(error, rawResponse, method, path) {
1825
1817
  switch (error.reason) {
1826
1818
  case "non-json":
1827
1819
  throw new CngApiError({
@@ -1835,7 +1827,7 @@ function handleNonStatusCodeError(error, rawResponse, method, path6) {
1835
1827
  rawResponse
1836
1828
  });
1837
1829
  case "timeout":
1838
- throw new CngApiTimeoutError(`Timeout exceeded when calling ${method} ${path6}.`, {
1830
+ throw new CngApiTimeoutError(`Timeout exceeded when calling ${method} ${path}.`, {
1839
1831
  cause: error.cause
1840
1832
  });
1841
1833
  case "unknown":
@@ -1987,10 +1979,10 @@ var HarnessFilesystemClient = class {
1987
1979
  return HttpResponsePromise.fromPromise(this.__getHarnessFilesystemGrep(request, requestOptions));
1988
1980
  }
1989
1981
  async __getHarnessFilesystemGrep(request, requestOptions) {
1990
- const { pattern, path: path6, include, output_mode: outputMode, context, case_insensitive: caseInsensitive } = request;
1982
+ const { pattern, path, include, output_mode: outputMode, context, case_insensitive: caseInsensitive } = request;
1991
1983
  const _queryParams = {
1992
1984
  pattern,
1993
- path: path6,
1985
+ path,
1994
1986
  include,
1995
1987
  output_mode: outputMode != null ? outputMode : void 0,
1996
1988
  context,
@@ -2043,10 +2035,10 @@ var HarnessFilesystemClient = class {
2043
2035
  return HttpResponsePromise.fromPromise(this.__getHarnessFilesystemGlob(request, requestOptions));
2044
2036
  }
2045
2037
  async __getHarnessFilesystemGlob(request, requestOptions) {
2046
- const { pattern, path: path6 } = request;
2038
+ const { pattern, path } = request;
2047
2039
  const _queryParams = {
2048
2040
  pattern,
2049
- path: path6
2041
+ path
2050
2042
  };
2051
2043
  const _headers = mergeHeaders(this._options?.headers, requestOptions?.headers);
2052
2044
  const _response = await fetcher({
@@ -2159,9 +2151,9 @@ var HarnessInternalKnowledgesClient = class {
2159
2151
  return HttpResponsePromise.fromPromise(this.__getHarnessSubskillAssetRawFile(request, requestOptions));
2160
2152
  }
2161
2153
  async __getHarnessSubskillAssetRawFile(request, requestOptions) {
2162
- const { path: path6, sha256 } = request;
2154
+ const { path, sha256 } = request;
2163
2155
  const _queryParams = {
2164
- path: path6,
2156
+ path,
2165
2157
  sha256
2166
2158
  };
2167
2159
  const _headers = mergeHeaders(this._options?.headers, requestOptions?.headers);
@@ -3211,7 +3203,7 @@ var logging;
3211
3203
  logging2.ConsoleLogger = ConsoleLogger;
3212
3204
  })(logging || (logging = {}));
3213
3205
 
3214
- // src/api.ts
3206
+ // src/shared/api-client.ts
3215
3207
  function createCngApiClient(options) {
3216
3208
  return new CngApiClient({
3217
3209
  baseUrl: resolveApiBaseUrl(options),
@@ -3235,1351 +3227,8 @@ async function readBackendHealth(options) {
3235
3227
  }
3236
3228
  }
3237
3229
 
3238
- // src/browser.ts
3239
- import { spawn } from "child_process";
3240
- import process2 from "process";
3241
- async function openBrowserUrl(url) {
3242
- const command = browserOpenCommand(url);
3243
- return await new Promise((resolve, reject) => {
3244
- const childProcess = spawn(command.command, command.args, {
3245
- detached: true,
3246
- stdio: "ignore"
3247
- });
3248
- childProcess.on("error", (error) => {
3249
- if (error.code === "ENOENT") {
3250
- resolve(false);
3251
- return;
3252
- }
3253
- reject(error);
3254
- });
3255
- childProcess.on("spawn", () => {
3256
- childProcess.unref();
3257
- resolve(true);
3258
- });
3259
- });
3260
- }
3261
- function browserOpenCommand(url) {
3262
- if (process2.platform === "darwin") {
3263
- return { command: "open", args: [url] };
3264
- }
3265
- if (process2.platform === "win32") {
3266
- return { command: "cmd", args: ["/c", "start", "", url] };
3267
- }
3268
- return { command: "xdg-open", args: [url] };
3269
- }
3270
-
3271
- // src/scrub/masker.ts
3272
- import fs from "fs/promises";
3273
- import path from "path";
3274
- async function scrubFindingsInline(targetPath, findings) {
3275
- const target = await resolveScrubTarget(targetPath);
3276
- const findingsByFile = groupFindingsBySafePath(target, findings);
3277
- let filesChanged = 0;
3278
- let replacements = 0;
3279
- for (const [absolutePath, fileFindings] of findingsByFile.safeFindingsByPath) {
3280
- const originalContent = await fs.readFile(absolutePath, "utf8");
3281
- let scrubbedContent = originalContent;
3282
- let fileReplacements = 0;
3283
- for (const finding of fileFindings) {
3284
- const placeholder = formatSecretPlaceholder(finding);
3285
- const nextContent = scrubbedContent.split(finding.rawSecret).join(placeholder);
3286
- if (nextContent !== scrubbedContent) {
3287
- fileReplacements += countOccurrences(scrubbedContent, finding.rawSecret);
3288
- scrubbedContent = nextContent;
3289
- }
3290
- }
3291
- if (scrubbedContent !== originalContent) {
3292
- await fs.writeFile(absolutePath, scrubbedContent);
3293
- filesChanged += 1;
3294
- replacements += fileReplacements;
3295
- }
3296
- }
3297
- return {
3298
- filesChanged,
3299
- replacements,
3300
- skippedFindings: findingsByFile.skippedFindings
3301
- };
3302
- }
3303
- function formatSecretPlaceholder(finding) {
3304
- return `[CNGKIT_SECRET:${sanitizePlaceholderPart(finding.detectorName)}:${finding.verified ? "verified" : "unverified"}]`;
3305
- }
3306
- function groupFindingsBySafePath(target, findings) {
3307
- const safeFindingsByPath = /* @__PURE__ */ new Map();
3308
- let skippedFindings = 0;
3309
- for (const finding of findings) {
3310
- const absolutePath = resolveSafeFindingPath(target, finding.filePath);
3311
- if (!absolutePath) {
3312
- skippedFindings += 1;
3313
- continue;
3314
- }
3315
- const fileFindings = safeFindingsByPath.get(absolutePath) ?? [];
3316
- fileFindings.push(finding);
3317
- safeFindingsByPath.set(absolutePath, fileFindings);
3318
- }
3319
- return { safeFindingsByPath, skippedFindings };
3320
- }
3321
- async function resolveScrubTarget(targetPath) {
3322
- const absoluteTargetPath = path.resolve(targetPath);
3323
- const stat = await fs.stat(absoluteTargetPath);
3324
- if (stat.isFile()) {
3325
- return {
3326
- rootDir: path.dirname(absoluteTargetPath),
3327
- allowedFilePath: absoluteTargetPath
3328
- };
3329
- }
3330
- return {
3331
- rootDir: absoluteTargetPath
3332
- };
3333
- }
3334
- function resolveSafeFindingPath(target, findingFilePath) {
3335
- const absolutePath = path.isAbsolute(findingFilePath) ? path.resolve(findingFilePath) : path.resolve(target.rootDir, findingFilePath);
3336
- if (target.allowedFilePath) {
3337
- return absolutePath === target.allowedFilePath ? absolutePath : void 0;
3338
- }
3339
- const normalizedRoot = `${path.resolve(target.rootDir)}${path.sep}`;
3340
- if (absolutePath === path.resolve(target.rootDir) || absolutePath.startsWith(normalizedRoot)) {
3341
- return absolutePath;
3342
- }
3343
- return void 0;
3344
- }
3345
- function countOccurrences(value, search) {
3346
- if (!search) {
3347
- return 0;
3348
- }
3349
- return value.split(search).length - 1;
3350
- }
3351
- function sanitizePlaceholderPart(value) {
3352
- return value.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "Unknown";
3353
- }
3354
-
3355
- // src/scrub/trufflehog.ts
3356
- import { spawn as spawn2 } from "child_process";
3357
- import path2 from "path";
3358
-
3359
- // src/scrub/findings.ts
3360
- function parseTruffleHogJsonLines(output) {
3361
- const findings = [];
3362
- for (const line of output.split(/\r?\n/)) {
3363
- const trimmedLine = line.trim();
3364
- if (!trimmedLine.startsWith("{")) {
3365
- continue;
3366
- }
3367
- const parsedLine = parseJsonRecord(trimmedLine);
3368
- if (!parsedLine) {
3369
- continue;
3370
- }
3371
- const finding = parseTruffleHogFinding(parsedLine);
3372
- if (finding) {
3373
- findings.push(finding);
3374
- }
3375
- }
3376
- return findings;
3377
- }
3378
- function parseTruffleHogFinding(finding) {
3379
- const rawSecret = readString(finding.Raw) || readString(finding.RawV2);
3380
- const filePath = readFilesystemFilePath(finding);
3381
- if (!rawSecret || !filePath) {
3382
- return void 0;
3383
- }
3384
- return {
3385
- detectorName: readString(finding.DetectorName) || "Unknown",
3386
- filePath,
3387
- line: readFilesystemLine(finding),
3388
- rawSecret,
3389
- redactedSecret: readString(finding.Redacted) || "",
3390
- verified: finding.Verified === true
3391
- };
3392
- }
3393
- function readFilesystemFilePath(finding) {
3394
- const filesystem = readFilesystemMetadata(finding);
3395
- if (!filesystem) {
3396
- return void 0;
3397
- }
3398
- return readString(filesystem.file) || readString(filesystem.path);
3399
- }
3400
- function readFilesystemLine(finding) {
3401
- const filesystem = readFilesystemMetadata(finding);
3402
- if (!filesystem) {
3403
- return void 0;
3404
- }
3405
- return typeof filesystem.line === "number" ? filesystem.line : void 0;
3406
- }
3407
- function readFilesystemMetadata(finding) {
3408
- const sourceMetadata = readRecord(finding.SourceMetadata);
3409
- const data = readRecord(sourceMetadata?.Data);
3410
- return readRecord(data?.Filesystem);
3411
- }
3412
- function parseJsonRecord(line) {
3413
- try {
3414
- return readRecord(JSON.parse(line));
3415
- } catch {
3416
- return void 0;
3417
- }
3418
- }
3419
- function isJsonRecord(value) {
3420
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
3421
- return false;
3422
- }
3423
- const prototype = Object.getPrototypeOf(value);
3424
- return prototype === Object.prototype || prototype === null;
3425
- }
3426
- function readRecord(value) {
3427
- return isJsonRecord(value) ? value : void 0;
3428
- }
3429
- function readString(value) {
3430
- return typeof value === "string" && value.length > 0 ? value : void 0;
3431
- }
3432
-
3433
- // src/scrub/trufflehog.ts
3434
- async function scanFilesystemWithTruffleHog(targetPath, options) {
3435
- const absoluteTargetPath = path2.resolve(options.cwd, targetPath);
3436
- const { stdout } = await runTruffleHog([
3437
- "filesystem",
3438
- absoluteTargetPath,
3439
- "--json",
3440
- "--no-update",
3441
- "--force-skip-binaries",
3442
- "--force-skip-archives"
3443
- ]);
3444
- return parseTruffleHogJsonLines(stdout);
3445
- }
3446
- function runTruffleHog(args) {
3447
- return new Promise((resolve, reject) => {
3448
- const child = spawn2("trufflehog", args, {
3449
- stdio: ["ignore", "pipe", "pipe"]
3450
- });
3451
- const stdoutChunks = [];
3452
- const stderrChunks = [];
3453
- child.stdout.on("data", (chunk) => {
3454
- stdoutChunks.push(chunk);
3455
- });
3456
- child.stderr.on("data", (chunk) => {
3457
- stderrChunks.push(chunk);
3458
- });
3459
- child.on("error", (error) => {
3460
- if (error.code === "ENOENT") {
3461
- reject(
3462
- new Error(
3463
- "TruffleHog is not installed. Install it with `brew install trufflehog` or see https://github.com/trufflesecurity/trufflehog."
3464
- )
3465
- );
3466
- return;
3467
- }
3468
- reject(error);
3469
- });
3470
- child.on("close", (exitCode) => {
3471
- const stdout = Buffer.concat(stdoutChunks).toString("utf8");
3472
- const stderr = Buffer.concat(stderrChunks).toString("utf8");
3473
- if (exitCode && exitCode !== 0) {
3474
- reject(new Error(`TruffleHog scan failed with exit code ${exitCode}: ${stderr.trim()}`));
3475
- return;
3476
- }
3477
- resolve({ stdout, stderr });
3478
- });
3479
- });
3480
- }
3481
-
3482
- // src/sync/client.ts
3483
- import process3 from "process";
3484
- import chokidar from "chokidar";
3485
- import WebSocket from "ws";
3486
-
3487
- // src/sync/files.ts
3488
- import fs2 from "fs/promises";
3489
- import path4 from "path";
3490
-
3491
- // src/sync/paths.ts
3492
- import { execFile } from "child_process";
3493
- import path3 from "path";
3494
- import { promisify } from "util";
3495
- var execFileAsync = promisify(execFile);
3496
- async function resolveRepoContext(cwd) {
3497
- try {
3498
- const { stdout } = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
3499
- cwd
3500
- });
3501
- return { rootDir: path3.resolve(stdout.trim()) };
3502
- } catch {
3503
- return { rootDir: path3.resolve(cwd) };
3504
- }
3505
- }
3506
- function toRepoRelativePath(rootDir, absolutePath) {
3507
- const relativePath = path3.relative(rootDir, absolutePath);
3508
- if (!relativePath || relativePath.startsWith("..") || path3.isAbsolute(relativePath)) {
3509
- return void 0;
3510
- }
3511
- return relativePath.split(path3.sep).join("/");
3512
- }
3513
- function resolveRepoPath(rootDir, relativePath) {
3514
- if (!isSafeRelativePath(relativePath)) {
3515
- return void 0;
3516
- }
3517
- const absolutePath = path3.resolve(rootDir, relativePath);
3518
- const normalizedRoot = `${path3.resolve(rootDir)}${path3.sep}`;
3519
- if (absolutePath !== path3.resolve(rootDir) && !absolutePath.startsWith(normalizedRoot)) {
3520
- return void 0;
3521
- }
3522
- return absolutePath;
3523
- }
3524
- function isSafeRelativePath(relativePath) {
3525
- if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\0")) {
3526
- return false;
3527
- }
3528
- const normalizedParts = relativePath.split(/[\\/]+/).filter(Boolean);
3529
- if (normalizedParts.includes("..")) {
3530
- return false;
3531
- }
3532
- return normalizedParts[0] !== ".git";
3533
- }
3534
- async function shouldSyncRelativePath(context, relativePath) {
3535
- if (!isSafeRelativePath(relativePath)) {
3536
- return false;
3537
- }
3538
- try {
3539
- await execFileAsync("git", ["check-ignore", "--quiet", "--", relativePath], {
3540
- cwd: context.rootDir
3541
- });
3542
- return false;
3543
- } catch (error) {
3544
- const exitCode = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
3545
- return exitCode === 1;
3546
- }
3547
- }
3548
-
3549
- // src/sync/files.ts
3550
- function createSuppressionTracker(windowMs = 1500) {
3551
- const suppressedUntilByPath = /* @__PURE__ */ new Map();
3552
- return {
3553
- suppress(relativePath) {
3554
- suppressedUntilByPath.set(relativePath, Date.now() + windowMs);
3555
- },
3556
- isSuppressed(relativePath) {
3557
- const suppressedUntil = suppressedUntilByPath.get(relativePath);
3558
- if (!suppressedUntil) {
3559
- return false;
3560
- }
3561
- if (suppressedUntil < Date.now()) {
3562
- suppressedUntilByPath.delete(relativePath);
3563
- return false;
3564
- }
3565
- return true;
3566
- }
3567
- };
3568
- }
3569
- async function* collectSnapshotMessages(context, peerId) {
3570
- yield* collectDirectorySnapshot(context, context.rootDir, peerId);
3571
- }
3572
- async function* collectDirectorySnapshot(context, directoryPath, peerId) {
3573
- const entries = await fs2.opendir(directoryPath);
3574
- for await (const entry of entries) {
3575
- const absolutePath = path4.join(directoryPath, entry.name);
3576
- const relativePath = toRepoRelativePath(context.rootDir, absolutePath);
3577
- if (!relativePath || !await shouldSyncRelativePath(context, relativePath)) {
3578
- continue;
3579
- }
3580
- if (entry.isDirectory()) {
3581
- yield* collectDirectorySnapshot(context, absolutePath, peerId);
3582
- continue;
3583
- }
3584
- if (!entry.isFile()) {
3585
- continue;
3586
- }
3587
- const [content, stat] = await Promise.all([fs2.readFile(absolutePath), fs2.stat(absolutePath)]);
3588
- yield {
3589
- type: "file",
3590
- peerId,
3591
- path: relativePath,
3592
- contentBase64: content.toString("base64"),
3593
- mtimeMs: stat.mtimeMs,
3594
- sentAt: Date.now()
3595
- };
3596
- }
3597
- }
3598
- async function buildFileMessage(context, peerId, relativePath) {
3599
- if (!await shouldSyncRelativePath(context, relativePath)) {
3600
- return void 0;
3601
- }
3602
- const absolutePath = resolveRepoPath(context.rootDir, relativePath);
3603
- if (!absolutePath) {
3604
- return void 0;
3605
- }
3606
- const stat = await fs2.stat(absolutePath);
3607
- if (!stat.isFile()) {
3608
- return void 0;
3609
- }
3610
- const content = await fs2.readFile(absolutePath);
3611
- return {
3612
- type: "file",
3613
- peerId,
3614
- path: relativePath,
3615
- contentBase64: content.toString("base64"),
3616
- mtimeMs: stat.mtimeMs,
3617
- sentAt: Date.now()
3618
- };
3619
- }
3620
- async function applyRemoteMessage(context, message, suppressionTracker) {
3621
- const absolutePath = resolveRepoPath(context.rootDir, message.path);
3622
- if (!absolutePath || !await shouldSyncRelativePath(context, message.path)) {
3623
- return;
3624
- }
3625
- suppressionTracker.suppress(message.path);
3626
- if (message.type === "delete") {
3627
- await fs2.rm(absolutePath, { force: true });
3628
- return;
3629
- }
3630
- await fs2.mkdir(path4.dirname(absolutePath), { recursive: true });
3631
- await fs2.writeFile(absolutePath, Buffer.from(message.contentBase64, "base64"));
3632
- }
3633
-
3634
- // src/sync/protocol.ts
3635
- import { z } from "zod";
3636
- var SyncBaseMessageSchema = z.object({
3637
- peerId: z.string().min(1),
3638
- sentAt: z.number().finite()
3639
- });
3640
- var SyncHelloMessageSchema = SyncBaseMessageSchema.extend({
3641
- type: z.literal("hello"),
3642
- role: z.union([z.literal("share"), z.literal("join")])
3643
- });
3644
- var SyncFileMessageSchema = SyncBaseMessageSchema.extend({
3645
- type: z.literal("file"),
3646
- path: z.string().min(1),
3647
- contentBase64: z.string(),
3648
- mtimeMs: z.number().finite()
3649
- });
3650
- var SyncDeleteMessageSchema = SyncBaseMessageSchema.extend({
3651
- type: z.literal("delete"),
3652
- path: z.string().min(1),
3653
- mtimeMs: z.number().finite()
3654
- });
3655
- var SyncSnapshotCompleteMessageSchema = SyncBaseMessageSchema.extend({
3656
- type: z.literal("snapshot-complete"),
3657
- fileCount: z.number().int().nonnegative()
3658
- });
3659
- var SyncMessageSchema = z.discriminatedUnion("type", [
3660
- SyncHelloMessageSchema,
3661
- SyncFileMessageSchema,
3662
- SyncDeleteMessageSchema,
3663
- SyncSnapshotCompleteMessageSchema
3664
- ]);
3665
- function encodeSyncMessage(message) {
3666
- return JSON.stringify(SyncMessageSchema.parse(message));
3667
- }
3668
- function decodeSyncMessage(value) {
3669
- if (typeof value !== "string") {
3670
- return void 0;
3671
- }
3672
- try {
3673
- return SyncMessageSchema.parse(JSON.parse(value));
3674
- } catch {
3675
- return void 0;
3676
- }
3677
- }
3678
-
3679
- // src/sync/client.ts
3680
- function createSyncWebSocketUrl(apiBaseUrl, roomCode) {
3681
- const url = new URL(apiBaseUrl);
3682
- url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
3683
- url.pathname = `/api/cng/sync/${encodeURIComponent(roomCode)}`;
3684
- url.search = "";
3685
- url.hash = "";
3686
- return url.toString();
3687
- }
3688
- function sendMessage(socket, message) {
3689
- if (socket.readyState === WebSocket.OPEN) {
3690
- socket.send(encodeSyncMessage(message));
3691
- }
3692
- }
3693
- async function startSyncSession(options) {
3694
- const repoContext = await resolveRepoContext(options.cwd);
3695
- const peerId = createPeerId();
3696
- const suppressionTracker = createSuppressionTracker();
3697
- const webSocketUrl = createSyncWebSocketUrl(options.apiBaseUrl, options.roomCode);
3698
- const socket = new WebSocket(webSocketUrl);
3699
- options.output.info(`Room: ${options.roomCode}`);
3700
- options.output.info(`Repo: ${repoContext.rootDir}`);
3701
- options.output.info(`Peer: ${peerId}`);
3702
- const watcherContext = {
3703
- repoContext,
3704
- socket,
3705
- peerId,
3706
- suppressionTracker,
3707
- output: options.output
3708
- };
3709
- const watcher = createRepoWatcher(watcherContext);
3710
- socket.on("open", () => {
3711
- sendMessage(socket, {
3712
- type: "hello",
3713
- peerId,
3714
- role: options.role,
3715
- sentAt: Date.now()
3716
- });
3717
- void sendInitialSnapshot(socket, repoContext, peerId, options.output);
3718
- });
3719
- socket.on("message", (data) => {
3720
- void handleRemoteMessage({
3721
- data,
3722
- repoContext,
3723
- peerId,
3724
- suppressionTracker,
3725
- output: options.output
3726
- });
3727
- });
3728
- await waitForSessionClose(socket, watcher);
3729
- }
3730
- function createRepoWatcher(context) {
3731
- const watcher = chokidar.watch(context.repoContext.rootDir, {
3732
- ignoreInitial: true,
3733
- ignored: (candidatePath) => {
3734
- const relativePath = toRepoRelativePath(context.repoContext.rootDir, candidatePath);
3735
- return relativePath?.split("/").includes(".git") ?? false;
3736
- }
3737
- });
3738
- watcher.on("add", (absolutePath) => {
3739
- void sendLocalFileChange(context, absolutePath);
3740
- });
3741
- watcher.on("change", (absolutePath) => {
3742
- void sendLocalFileChange(context, absolutePath);
3743
- });
3744
- watcher.on("unlink", (absolutePath) => {
3745
- void sendLocalDelete(context, absolutePath);
3746
- });
3747
- return watcher;
3748
- }
3749
- async function sendInitialSnapshot(socket, repoContext, peerId, output) {
3750
- let fileCount = 0;
3751
- for await (const message of collectSnapshotMessages(repoContext, peerId)) {
3752
- sendMessage(socket, message);
3753
- fileCount += 1;
3754
- }
3755
- sendMessage(socket, {
3756
- type: "snapshot-complete",
3757
- peerId,
3758
- sentAt: Date.now(),
3759
- fileCount
3760
- });
3761
- output.info(`sent snapshot ${fileCount} files`);
3762
- }
3763
- async function sendLocalFileChange(context, absolutePath) {
3764
- const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);
3765
- if (!relativePath || context.suppressionTracker.isSuppressed(relativePath)) {
3766
- return;
3767
- }
3768
- const message = await buildFileMessage(context.repoContext, context.peerId, relativePath);
3769
- if (!message) {
3770
- return;
3771
- }
3772
- sendMessage(context.socket, message);
3773
- context.output.info(`sent file ${relativePath}`);
3774
- }
3775
- async function sendLocalDelete(context, absolutePath) {
3776
- const relativePath = toRepoRelativePath(context.repoContext.rootDir, absolutePath);
3777
- if (!relativePath || context.suppressionTracker.isSuppressed(relativePath)) {
3778
- return;
3779
- }
3780
- if (!await shouldSyncRelativePath(context.repoContext, relativePath)) {
3781
- return;
3782
- }
3783
- sendMessage(context.socket, {
3784
- type: "delete",
3785
- peerId: context.peerId,
3786
- path: relativePath,
3787
- mtimeMs: Date.now(),
3788
- sentAt: Date.now()
3789
- });
3790
- context.output.info(`sent delete ${relativePath}`);
3791
- }
3792
- async function handleRemoteMessage(options) {
3793
- const decodedMessage = decodeSyncMessage(options.data.toString());
3794
- if (!decodedMessage || decodedMessage.peerId === options.peerId) {
3795
- return;
3796
- }
3797
- if (decodedMessage.type === "hello") {
3798
- options.output.info(`peer joined ${decodedMessage.peerId}`);
3799
- return;
3800
- }
3801
- if (decodedMessage.type === "snapshot-complete") {
3802
- options.output.info(`peer snapshot complete ${decodedMessage.fileCount} files`);
3803
- return;
3804
- }
3805
- await applyRemoteMessage(options.repoContext, decodedMessage, options.suppressionTracker);
3806
- options.output.info(`applied ${decodedMessage.type} ${decodedMessage.path}`);
3807
- }
3808
- async function waitForSessionClose(socket, watcher) {
3809
- await new Promise((resolve, reject) => {
3810
- let settled = false;
3811
- const closeSession = async () => {
3812
- await watcher.close();
3813
- if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
3814
- socket.close();
3815
- }
3816
- };
3817
- const finish = async () => {
3818
- if (settled) {
3819
- return;
3820
- }
3821
- settled = true;
3822
- process3.off("SIGINT", onSignal);
3823
- process3.off("SIGTERM", onSignal);
3824
- await closeSession();
3825
- resolve();
3826
- };
3827
- const onSignal = () => {
3828
- void finish();
3829
- };
3830
- socket.on("error", async (error) => {
3831
- if (settled) {
3832
- return;
3833
- }
3834
- settled = true;
3835
- process3.off("SIGINT", onSignal);
3836
- process3.off("SIGTERM", onSignal);
3837
- await closeSession();
3838
- reject(error);
3839
- });
3840
- socket.on("close", () => {
3841
- void finish();
3842
- });
3843
- process3.on("SIGINT", onSignal);
3844
- process3.on("SIGTERM", onSignal);
3845
- });
3846
- }
3847
-
3848
- // src/transcripts/reader.ts
3849
- import { promises as fs3 } from "fs";
3850
- import os from "os";
3851
- import path5 from "path";
3852
- var TranscriptSourceNames = ["codex", "claude"];
3853
- async function listTranscriptRecords(options) {
3854
- const files = await discoverTranscriptFiles(options.source);
3855
- const records = await Promise.all(files.slice(0, options.limit).map(readTranscriptRecord));
3856
- return records;
3857
- }
3858
- async function readTranscriptEntries(options) {
3859
- const file = await resolveTranscriptFile(options.source, options.target);
3860
- const entries = await readEntriesFromFile(file, options.includeInternal);
3861
- return entries.slice(Math.max(0, entries.length - options.limit));
3862
- }
3863
- async function grepTranscriptEntries(options) {
3864
- const files = await discoverTranscriptFiles(options.source);
3865
- const normalizedQuery = options.query.toLowerCase();
3866
- const matches = [];
3867
- for (const file of files.slice(0, options.fileLimit)) {
3868
- const entries = await readEntriesFromFile(file, options.includeInternal);
3869
- for (const entry of entries) {
3870
- if (entry.text.toLowerCase().includes(normalizedQuery)) {
3871
- matches.push(entry);
3872
- }
3873
- if (matches.length >= options.limit) {
3874
- return matches;
3875
- }
3876
- }
3877
- }
3878
- return matches;
3879
- }
3880
- async function discoverTranscriptFiles(source) {
3881
- const sources = source === "all" ? TranscriptSourceNames : [source];
3882
- const records = await Promise.all(sources.flatMap((sourceName) => sourceRoots(sourceName)));
3883
- return records.flat().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
3884
- }
3885
- function sourceRoots(source) {
3886
- if (source === "codex") {
3887
- return [
3888
- discoverJsonlFiles("codex", expandHome("~/.codex/sessions")),
3889
- discoverJsonlFiles("codex", expandHome("~/.codex/archived_sessions"))
3890
- ];
3891
- }
3892
- return [
3893
- discoverJsonlFiles("claude", expandHome("~/.claude/projects")),
3894
- discoverJsonlFiles("claude", expandHome("~/.claude/history.jsonl"))
3895
- ];
3896
- }
3897
- async function discoverJsonlFiles(source, rootPath) {
3898
- const stat = await statIfExists(rootPath);
3899
- if (!stat) {
3900
- return [];
3901
- }
3902
- if (stat.isFile()) {
3903
- return [await recordFromPath(source, rootPath, stat.mtime)];
3904
- }
3905
- const records = [];
3906
- const entries = await fs3.readdir(rootPath, { withFileTypes: true });
3907
- for (const entry of entries) {
3908
- const entryPath = path5.join(rootPath, entry.name);
3909
- if (entry.isDirectory()) {
3910
- records.push(...await discoverJsonlFiles(source, entryPath));
3911
- continue;
3912
- }
3913
- if (entry.isFile() && entry.name.endsWith(".jsonl")) {
3914
- const entryStat = await fs3.stat(entryPath);
3915
- records.push(await recordFromPath(source, entryPath, entryStat.mtime));
3916
- }
3917
- }
3918
- return records;
3919
- }
3920
- async function recordFromPath(source, filePath, mtime) {
3921
- return {
3922
- source,
3923
- filePath,
3924
- updatedAt: mtime.toISOString()
3925
- };
3926
- }
3927
- async function readTranscriptRecord(record) {
3928
- const metadata = await readMetadataFromFile(record.source, record.filePath);
3929
- return {
3930
- ...record,
3931
- ...metadata
3932
- };
3933
- }
3934
- async function readMetadataFromFile(source, filePath) {
3935
- const lines = await readJsonlLines(filePath, 80);
3936
- for (const line of lines) {
3937
- const parsed = parseJsonObject(line);
3938
- if (!parsed) {
3939
- continue;
3940
- }
3941
- if (source === "codex") {
3942
- const payload = asObject(parsed.payload);
3943
- if (parsed.type === "session_meta" && payload) {
3944
- return {
3945
- cwd: readString2(payload.cwd),
3946
- sessionId: readString2(payload.session_id) ?? readString2(payload.id),
3947
- title: readString2(payload.cwd)
3948
- };
3949
- }
3950
- }
3951
- if (source === "claude") {
3952
- const message = asObject(parsed.message);
3953
- const role = readString2(message?.role) ?? readString2(parsed.type);
3954
- const content = message ? extractContentText(message.content) : readString2(parsed.content);
3955
- if (content && (role === "user" || role === "assistant")) {
3956
- return {
3957
- cwd: readString2(parsed.cwd) ?? readString2(parsed.project),
3958
- sessionId: readString2(parsed.sessionId),
3959
- title: truncateSingleLine(content, 120)
3960
- };
3961
- }
3962
- }
3963
- }
3964
- return {
3965
- sessionId: path5.basename(filePath, ".jsonl")
3966
- };
3967
- }
3968
- async function resolveTranscriptFile(source, target) {
3969
- const expandedTarget = expandHome(target);
3970
- const directStat = await statIfExists(expandedTarget);
3971
- if (directStat?.isFile()) {
3972
- return recordFromPath(detectSourceFromPath(expandedTarget), expandedTarget, directStat.mtime);
3973
- }
3974
- const files = await discoverTranscriptFiles(source);
3975
- const normalizedTarget = target.toLowerCase();
3976
- const match = files.find((file) => {
3977
- return file.filePath.toLowerCase().includes(normalizedTarget) || file.sessionId?.toLowerCase().includes(normalizedTarget);
3978
- });
3979
- if (!match) {
3980
- throw new Error(`No transcript matched "${target}". Run cngkit transcripts list first.`);
3981
- }
3982
- return match;
3983
- }
3984
- async function readEntriesFromFile(record, includeInternal) {
3985
- const content = await fs3.readFile(record.filePath, "utf8");
3986
- const entries = [];
3987
- for (const line of content.split("\n")) {
3988
- if (!line.trim()) {
3989
- continue;
3990
- }
3991
- const parsed = parseJsonObject(line);
3992
- if (!parsed) {
3993
- continue;
3994
- }
3995
- const entry = record.source === "codex" ? parseCodexEntry(record, parsed, includeInternal) : parseClaudeEntry(record, parsed, includeInternal);
3996
- if (entry) {
3997
- entries.push(entry);
3998
- }
3999
- }
4000
- return entries;
4001
- }
4002
- function parseCodexEntry(record, parsed, includeInternal) {
4003
- const payload = asObject(parsed.payload);
4004
- if (!payload || parsed.type !== "response_item") {
4005
- return void 0;
4006
- }
4007
- const role = readString2(payload.role);
4008
- if (!role || !includeInternal && role !== "user" && role !== "assistant") {
4009
- return void 0;
4010
- }
4011
- const text = extractContentText(payload.content);
4012
- if (!text) {
4013
- return void 0;
4014
- }
4015
- return {
4016
- source: record.source,
4017
- filePath: record.filePath,
4018
- timestamp: readString2(parsed.timestamp),
4019
- role,
4020
- text
4021
- };
4022
- }
4023
- function parseClaudeEntry(record, parsed, includeInternal) {
4024
- const message = asObject(parsed.message);
4025
- const role = readString2(message?.role) ?? readString2(parsed.type);
4026
- if (!role || !includeInternal && role !== "user" && role !== "assistant") {
4027
- return void 0;
4028
- }
4029
- const text = message ? extractContentText(message.content) : readString2(parsed.content);
4030
- if (!text) {
4031
- return void 0;
4032
- }
4033
- return {
4034
- source: record.source,
4035
- filePath: record.filePath,
4036
- timestamp: readString2(parsed.timestamp),
4037
- role,
4038
- text
4039
- };
4040
- }
4041
- function extractContentText(value) {
4042
- if (typeof value === "string") {
4043
- return value;
4044
- }
4045
- if (!Array.isArray(value)) {
4046
- return void 0;
4047
- }
4048
- const parts = value.flatMap((item) => {
4049
- if (typeof item === "string") {
4050
- return [item];
4051
- }
4052
- const objectItem = asObject(item);
4053
- if (!objectItem) {
4054
- return [];
4055
- }
4056
- return [readString2(objectItem.text), readString2(objectItem.content)].filter(
4057
- (part) => Boolean(part)
4058
- );
4059
- });
4060
- return parts.length > 0 ? parts.join("\n") : void 0;
4061
- }
4062
- async function readJsonlLines(filePath, limit) {
4063
- const content = await fs3.readFile(filePath, "utf8");
4064
- return content.split("\n").filter(Boolean).slice(0, limit);
4065
- }
4066
- async function statIfExists(filePath) {
4067
- try {
4068
- return await fs3.stat(filePath);
4069
- } catch (error) {
4070
- if (isNodeError(error) && error.code === "ENOENT") {
4071
- return void 0;
4072
- }
4073
- throw error;
4074
- }
4075
- }
4076
- function parseJsonObject(line) {
4077
- try {
4078
- const parsed = JSON.parse(line);
4079
- return asObject(parsed);
4080
- } catch {
4081
- return void 0;
4082
- }
4083
- }
4084
- function asObject(value) {
4085
- if (value && typeof value === "object" && !Array.isArray(value)) {
4086
- return value;
4087
- }
4088
- return void 0;
4089
- }
4090
- function readString2(value) {
4091
- return typeof value === "string" && value.trim() ? value : void 0;
4092
- }
4093
- function isNodeError(error) {
4094
- return error instanceof Error && "code" in error;
4095
- }
4096
- function expandHome(value) {
4097
- if (value === "~") {
4098
- return os.homedir();
4099
- }
4100
- if (value.startsWith("~/")) {
4101
- return path5.join(os.homedir(), value.slice(2));
4102
- }
4103
- return value;
4104
- }
4105
- function detectSourceFromPath(filePath) {
4106
- return filePath.includes(`${path5.sep}.claude${path5.sep}`) ? "claude" : "codex";
4107
- }
4108
- function truncateSingleLine(value, maxLength) {
4109
- const line = value.replace(/\s+/g, " ").trim();
4110
- return line.length > maxLength ? `${line.slice(0, maxLength - 1)}...` : line;
4111
- }
4112
-
4113
- // src/command-actions.ts
4114
- async function runLoginCommand(options, output) {
4115
- const loginUrl = new URL("/login", resolveApiBaseUrl(options));
4116
- const loginUrlString = loginUrl.toString();
4117
- output.info(`Opening ${loginUrlString}`);
4118
- const didOpenBrowser = await openBrowserUrl(loginUrlString);
4119
- if (!didOpenBrowser) {
4120
- output.info(`No browser opener found. Open this URL manually: ${loginUrlString}`);
4121
- }
4122
- }
4123
- async function runShareCommand(roomCode, options, output) {
4124
- const syncRoomCode = roomCode ?? createRoomCode();
4125
- output.info(`Share code: ${syncRoomCode}`);
4126
- await printBackendStatus(options, output);
4127
- await runSyncSession("share", syncRoomCode, options, output);
4128
- }
4129
- async function runJoinCommand(roomCode, options, output) {
4130
- if (!roomCode) {
4131
- throw new Error("Missing room code. Usage: cngkit coderoom join <room-code>");
4132
- }
4133
- await printBackendStatus(options, output);
4134
- await runSyncSession("join", roomCode, options, output);
4135
- }
4136
- async function runScrubCommand(targetPath, options, output, dependencies) {
4137
- const scrubTargetPath = targetPath ?? ".";
4138
- const scanFilesystem = dependencies?.scanFilesystem ?? ((scanTargetPath) => scanFilesystemWithTruffleHog(scanTargetPath, {
4139
- cwd: process4.cwd()
4140
- }));
4141
- const findings = await scanFilesystem(scrubTargetPath);
4142
- if (findings.length === 0) {
4143
- output.info("No secrets found.");
4144
- return;
4145
- }
4146
- output.info(formatScrubReport(findings));
4147
- if (!options.yes) {
4148
- if (options.mask) {
4149
- throw new Error("Inline scrubbing rewrites files. Re-run with --yes to continue.");
4150
- }
4151
- output.info("Report only. Re-run with --yes to scrub files inline.");
4152
- return;
4153
- }
4154
- const result = await scrubFindingsInline(scrubTargetPath, findings);
4155
- output.info(
4156
- `Masked ${result.replacements} secret${result.replacements === 1 ? "" : "s"} in ${result.filesChanged} file${result.filesChanged === 1 ? "" : "s"}.`
4157
- );
4158
- if (result.skippedFindings > 0) {
4159
- output.info(`Skipped ${result.skippedFindings} finding(s) outside the scrub root.`);
4160
- }
4161
- }
4162
- async function runTranscriptCommand(args, options, output) {
4163
- const [action = "list", ...actionArgs] = args ?? [];
4164
- const source = normalizeTranscriptSource(options.source);
4165
- const limit = coerceLimit(options.limit, action === "list" ? 12 : 80, 500);
4166
- const includeInternal = options.includeInternal === true;
4167
- switch (action) {
4168
- case "help":
4169
- output.info(formatCngkitHelp("transcripts"));
4170
- return;
4171
- case "list": {
4172
- const records = await listTranscriptRecords({ source, limit });
4173
- if (options.json) {
4174
- output.info(formatJson({ records }));
4175
- return;
4176
- }
4177
- output.info(formatTranscriptRecords(records));
4178
- return;
4179
- }
4180
- case "read": {
4181
- const target = actionArgs[0];
4182
- if (!target) {
4183
- throw new Error("Missing transcript target. Usage: cngkit transcripts read <path-or-id>");
4184
- }
4185
- const entries = await readTranscriptEntries({
4186
- source,
4187
- target,
4188
- limit,
4189
- includeInternal
4190
- });
4191
- if (options.json) {
4192
- output.info(formatJson({ entries }));
4193
- return;
4194
- }
4195
- output.info(formatTranscriptEntries(entries));
4196
- return;
4197
- }
4198
- case "grep": {
4199
- const query = optionalJoinedArgument(actionArgs);
4200
- if (!query) {
4201
- throw new Error("Missing query. Usage: cngkit transcripts grep <query>");
4202
- }
4203
- const entries = await grepTranscriptEntries({
4204
- source,
4205
- query,
4206
- limit,
4207
- fileLimit: coerceLimit(options.fileLimit, 60, 5e3),
4208
- includeInternal
4209
- });
4210
- if (options.json) {
4211
- output.info(formatJson({ entries }));
4212
- return;
4213
- }
4214
- output.info(formatTranscriptEntries(entries));
4215
- return;
4216
- }
4217
- default:
4218
- throw new Error("Unknown transcripts command. Usage: cngkit transcripts <list|read|grep>");
4219
- }
4220
- }
4221
- async function runKnowStatusCommand(options, output, dependencies) {
4222
- const api = dependencies?.api ?? createKnowledgesApi(options);
4223
- const response = await api.getCatalog();
4224
- if (options.json) {
4225
- output.info(formatJson(response.data));
4226
- return;
4227
- }
4228
- const latestRun = response.data.latestRun;
4229
- const lines = [
4230
- `Catalog: ${response.data.name}`,
4231
- `Files: ${response.data.files}`,
4232
- `Blobs: ${response.data.blobs}`,
4233
- `Vectorize: ${response.data.vectorize.index} (${response.data.vectorize.binding})`
4234
- ];
4235
- if (latestRun) {
4236
- lines.push(`Latest run: ${latestRun.status} (${latestRun.id})`);
4237
- if (latestRun.updated_at) {
4238
- lines.push(`Updated: ${latestRun.updated_at}`);
4239
- }
4240
- } else {
4241
- lines.push("Latest run: none");
4242
- }
4243
- output.info(lines.join("\n"));
4244
- }
4245
- async function runKnowAudiencesCommand(options, output, dependencies) {
4246
- const api = dependencies?.api ?? createKnowledgesApi(options);
4247
- const response = await api.listAudiences();
4248
- if (options.json) {
4249
- output.info(formatJson(response.data));
4250
- return;
4251
- }
4252
- const lines = response.data.audiences.flatMap((audience) => [
4253
- `${audience.id} - ${audience.label}`,
4254
- ` ${singleLine(audience.help)}`
4255
- ]);
4256
- output.info(lines.length > 0 ? lines.join("\n") : "No audiences available.");
4257
- }
4258
- async function runKnowSearchCommand(query, options, output, dependencies) {
4259
- if (!query) {
4260
- throw new Error("Missing search query. Usage: cngkit knowledges search <query>");
4261
- }
4262
- const api = dependencies?.api ?? createKnowledgesApi(options);
4263
- const response = await api.search(query, coerceLimit(options.limit, 5));
4264
- if (options.json) {
4265
- output.info(formatJson(response.data));
4266
- return;
4267
- }
4268
- const lines = response.data.results.flatMap((result, index) => [
4269
- `${index + 1}. ${result.title} (${result.subskillName})`,
4270
- ` score ${result.score.toFixed(3)} | ${result.path}`,
4271
- ` ${singleLine(result.description || result.contentPreview)}`
4272
- ]);
4273
- output.info(lines.length > 0 ? lines.join("\n") : `No results for "${query}".`);
4274
- }
4275
- async function runKnowListCommand(query, options, output, dependencies) {
4276
- const api = dependencies?.api ?? createKnowledgesApi(options);
4277
- const response = await api.listSubskills();
4278
- const limit = coerceLimit(options.limit, 25);
4279
- const normalizedQuery = query?.toLowerCase();
4280
- const subskills = response.data.subskills.filter((subskill) => {
4281
- if (!normalizedQuery) {
4282
- return true;
4283
- }
4284
- return [subskill.name, subskill.title, subskill.description, subskill.type].join(" ").toLowerCase().includes(normalizedQuery);
4285
- }).slice(0, limit);
4286
- if (options.json) {
4287
- output.info(formatJson({ subskills, total: subskills.length }));
4288
- return;
4289
- }
4290
- const lines = subskills.flatMap((subskill) => [
4291
- `${subskill.name} [${subskill.type}]`,
4292
- ` ${subskill.title} | files ${subskill.fileCount} | rating ${subskill.rating}`,
4293
- ` ${singleLine(subskill.description)}`
4294
- ]);
4295
- output.info(lines.length > 0 ? lines.join("\n") : "No matching subskills.");
4296
- }
4297
- async function runKnowFilesCommand(query, options, output, dependencies) {
4298
- const api = dependencies?.api ?? createKnowledgesApi(options);
4299
- const response = await api.listFiles({
4300
- query,
4301
- audience: normalizeAudienceId(options.audience),
4302
- limit: coerceLimit(options.limit, 25)
4303
- });
4304
- if (options.json) {
4305
- output.info(formatJson(response.data));
4306
- return;
4307
- }
4308
- const lines = response.data.files.map((file) => {
4309
- const title = file.display_title ?? file.title ?? file.subskill_name ?? "Untitled";
4310
- return `${file.path}
4311
- ${title}`;
4312
- });
4313
- output.info(lines.length > 0 ? lines.join("\n") : "No matching files.");
4314
- }
4315
- async function runKnowReadCommand(filePath, options, output, dependencies) {
4316
- if (!filePath) {
4317
- throw new Error("Missing file_path. Usage: cngkit knowledges read <file_path>");
4318
- }
4319
- const api = dependencies?.api ?? createKnowledgesApi(options);
4320
- const response = await api.read({
4321
- filePath: normalizeCatalogPath(filePath),
4322
- offset: coerceOptionalNumber(options.offset),
4323
- limit: coerceLimit(options.limit, 200, 2e3)
4324
- });
4325
- if (options.json) {
4326
- output.info(formatJson(response.data));
4327
- return;
4328
- }
4329
- output.info(response.data.content);
4330
- if (response.data.truncated) {
4331
- output.info(
4332
- `[truncated: showing ${response.data.limit} lines from offset ${response.data.offset} of ${response.data.total_lines}]`
4333
- );
4334
- }
4335
- }
4336
- async function runKnowGrepCommand(pattern, options, output, dependencies) {
4337
- if (!pattern) {
4338
- throw new Error("Missing pattern. Usage: cngkit knowledges grep <pattern>");
4339
- }
4340
- const api = dependencies?.api ?? createKnowledgesApi(options);
4341
- const limit = options.limit !== void 0 ? coerceOptionalNumber(options.limit) : void 0;
4342
- const response = await api.grep({
4343
- pattern,
4344
- path: normalizeCatalogPath(options.path ?? "/"),
4345
- include: options.include ?? "*",
4346
- mode: normalizeGrepMode(options.outputMode),
4347
- context: coerceLimit(options.context, 0, 20),
4348
- ignoreCase: options.caseInsensitive === true
4349
- });
4350
- if (options.json) {
4351
- output.info(formatJson(response.data));
4352
- return;
4353
- }
4354
- if (response.data.mode === "files_with_matches") {
4355
- const files = limit ? response.data.files.slice(0, limit) : response.data.files;
4356
- output.info(
4357
- files.length > 0 ? files.join("\n") : `No files matched "${pattern}".`
4358
- );
4359
- return;
4360
- }
4361
- if (response.data.mode === "count") {
4362
- const counts = limit ? response.data.counts.slice(0, limit) : response.data.counts;
4363
- const lines2 = counts.map((count) => `${count.file_path}: ${count.match_count}`);
4364
- output.info(lines2.length > 0 ? lines2.join("\n") : `No matches for "${pattern}".`);
4365
- return;
4366
- }
4367
- const matches = limit ? response.data.matches.slice(0, limit) : response.data.matches;
4368
- const lines = matches.flatMap((match) => [
4369
- `${match.file_path}:${match.line_number}`,
4370
- ...match.context_before.map((line) => ` ${line}`),
4371
- `> ${match.line}`,
4372
- ...match.context_after.map((line) => ` ${line}`)
4373
- ]);
4374
- output.info(lines.length > 0 ? lines.join("\n") : `No matches for "${pattern}".`);
4375
- }
4376
- async function runKnowGlobCommand(pattern, options, output, dependencies) {
4377
- const api = dependencies?.api ?? createKnowledgesApi(options);
4378
- const limit = options.limit !== void 0 ? coerceOptionalNumber(options.limit) : void 0;
4379
- const response = await api.glob({
4380
- pattern: pattern ?? "**/*.md",
4381
- path: normalizeCatalogPath(options.path ?? "/")
4382
- });
4383
- if (options.json) {
4384
- output.info(formatJson(response.data));
4385
- return;
4386
- }
4387
- const files = limit ? response.data.files.slice(0, limit) : response.data.files;
4388
- const wasClientTruncated = limit ? response.data.files.length > limit : false;
4389
- output.info(files.length > 0 ? files.join("\n") : "No matching files.");
4390
- if (response.data.truncated || wasClientTruncated) {
4391
- output.info(`[truncated: ${response.data.total_files} total files]`);
4392
- }
4393
- }
4394
- async function printBackendStatus(options, output) {
4395
- const health = await readBackendHealth(options);
4396
- output.info(health.ok ? `API: ${health.service} ready` : `API: unavailable (${health.message})`);
4397
- }
4398
- async function runSyncSession(role, roomCode, options, output) {
4399
- await startSyncSession({
4400
- apiBaseUrl: resolveApiBaseUrl(options),
4401
- roomCode,
4402
- role,
4403
- cwd: process4.cwd(),
4404
- output
4405
- });
4406
- }
4407
- function formatScrubReport(findings) {
4408
- const lines = [`Found ${findings.length} possible secret${findings.length === 1 ? "" : "s"}:`];
4409
- for (const finding of findings) {
4410
- const location = finding.line ? `${finding.filePath}:${finding.line}` : finding.filePath;
4411
- const status = finding.verified ? "verified" : "unverified";
4412
- lines.push(`- ${location} ${finding.detectorName} ${status}`);
4413
- }
4414
- return lines.join("\n");
4415
- }
4416
- function formatTranscriptRecords(records) {
4417
- if (records.length === 0) {
4418
- return "No transcript files found.";
4419
- }
4420
- return records.map((record, index) => {
4421
- const title = record.title ? `
4422
- ${singleLine(record.title)}` : "";
4423
- const cwd = record.cwd ? `
4424
- cwd ${record.cwd}` : "";
4425
- const session = record.sessionId ? `
4426
- session ${record.sessionId}` : "";
4427
- return `${index + 1}. ${record.source} ${record.updatedAt}
4428
- ${record.filePath}${session}${cwd}${title}`;
4429
- }).join("\n");
4430
- }
4431
- function formatTranscriptEntries(entries) {
4432
- if (entries.length === 0) {
4433
- return "No transcript entries found.";
4434
- }
4435
- return entries.map((entry) => {
4436
- const when = entry.timestamp ? ` ${entry.timestamp}` : "";
4437
- return `[${entry.source}] ${entry.role}${when}
4438
- ${entry.text.trim()}`;
4439
- }).join("\n\n");
4440
- }
4441
- function createKnowledgesApi(options) {
4442
- const client = createCngApiClient(options);
4443
- return {
4444
- getCatalog: () => client.harnessKnowledges.getHarnessKnowledgesCatalog(),
4445
- listAudiences: () => client.harnessKnowledges.listHarnessKnowledgesAudiences(),
4446
- search: (query, limit) => client.harnessKnowledges.searchHarnessKnowledges({
4447
- q: query,
4448
- limit
4449
- }),
4450
- listSubskills: () => client.harnessKnowledges.listHarnessKnowledgesSubskills(),
4451
- listFiles: ({ query, audience, limit }) => client.harnessKnowledges.listHarnessSubskillAssets({
4452
- ...query ? { q: query } : {},
4453
- ...audience ? { audience } : {},
4454
- limit
4455
- }),
4456
- read: ({ filePath, offset, limit }) => client.harnessFilesystem.getHarnessFilesystemRead({
4457
- file_path: filePath,
4458
- offset,
4459
- limit
4460
- }),
4461
- grep: ({ pattern, path: path6, include, mode, context, ignoreCase }) => client.harnessFilesystem.getHarnessFilesystemGrep({
4462
- pattern,
4463
- path: path6,
4464
- include,
4465
- output_mode: mode,
4466
- context,
4467
- case_insensitive: ignoreCase ? "true" : void 0
4468
- }),
4469
- glob: ({ pattern, path: path6 }) => client.harnessFilesystem.getHarnessFilesystemGlob({
4470
- pattern,
4471
- path: path6
4472
- })
4473
- };
4474
- }
4475
- function coerceLimit(value, defaultValue, maxValue = 100) {
4476
- const normalizedValue = coerceOptionalNumber(value);
4477
- if (normalizedValue === void 0 || Number.isNaN(normalizedValue)) {
4478
- return defaultValue;
4479
- }
4480
- return Math.max(defaultValue === 0 ? 0 : 1, Math.min(maxValue, Math.trunc(normalizedValue)));
4481
- }
4482
- function coerceOptionalNumber(value) {
4483
- if (value === void 0 || value === "") {
4484
- return void 0;
4485
- }
4486
- return Number(value);
4487
- }
4488
- function normalizeAudienceId(value) {
4489
- if (value === void 0) {
4490
- return void 0;
4491
- }
4492
- switch (value) {
4493
- case "all":
4494
- case "operators":
4495
- case "builders":
4496
- case "researchers":
4497
- case "agent-makers":
4498
- return value;
4499
- default:
4500
- throw new Error(
4501
- `Unknown audience "${value}". Run cngkit knowledges audiences to see supported values.`
4502
- );
4503
- }
4504
- }
4505
- function normalizeGrepMode(value) {
4506
- if (value === void 0) {
4507
- return "content";
4508
- }
4509
- switch (value) {
4510
- case "content":
4511
- case "files_with_matches":
4512
- case "count":
4513
- return value;
4514
- default:
4515
- throw new Error("Unknown grep mode. Use one of: content, files_with_matches, count.");
4516
- }
4517
- }
4518
- function normalizeTranscriptSource(value) {
4519
- if (value === void 0) {
4520
- return "all";
4521
- }
4522
- switch (value) {
4523
- case "all":
4524
- case "codex":
4525
- case "claude":
4526
- return value;
4527
- default:
4528
- throw new Error("Unknown transcript source. Use one of: all, codex, claude.");
4529
- }
4530
- }
4531
- function singleLine(value) {
4532
- return value.replace(/\s+/g, " ").trim();
4533
- }
4534
- function formatJson(value) {
4535
- return JSON.stringify(value, null, 2);
4536
- }
4537
- function optionalJoinedArgument(values) {
4538
- return values.length > 0 ? values.join(" ") : void 0;
4539
- }
4540
- function normalizeCatalogPath(value) {
4541
- const trimmed = value.trim();
4542
- if (trimmed === "/" || trimmed === "") {
4543
- return "skills";
4544
- }
4545
- const relativePath = trimmed.replace(/^\/+/, "");
4546
- if (relativePath.startsWith("skills/knowledges/")) {
4547
- return relativePath;
4548
- }
4549
- const [firstSegment, ...restSegments] = relativePath.split("/");
4550
- if (!firstSegment) {
4551
- return relativePath;
4552
- }
4553
- switch (firstSegment) {
4554
- case "concepts":
4555
- case "domains":
4556
- case "formats":
4557
- case "languages":
4558
- case "libraries":
4559
- case "patterns":
4560
- case "platforms":
4561
- case "procedures":
4562
- case "protocols":
4563
- case "tools":
4564
- return `skills/knowledges/subskills/${firstSegment}${restSegments.length > 0 ? `/${restSegments.join("/")}` : ""}`;
4565
- default:
4566
- return relativePath;
4567
- }
4568
- }
4569
-
4570
3230
  export {
4571
- runLoginCommand,
4572
- runShareCommand,
4573
- runJoinCommand,
4574
- runScrubCommand,
4575
- runTranscriptCommand,
4576
- runKnowStatusCommand,
4577
- runKnowAudiencesCommand,
4578
- runKnowSearchCommand,
4579
- runKnowListCommand,
4580
- runKnowFilesCommand,
4581
- runKnowReadCommand,
4582
- runKnowGrepCommand,
4583
- runKnowGlobCommand
3231
+ createCngApiClient,
3232
+ readBackendHealth
4584
3233
  };
4585
- //# sourceMappingURL=chunk-Z3J7PPZB.js.map
3234
+ //# sourceMappingURL=chunk-QEZQGKFX.js.map