@crouton-kit/capture 1.3.4 → 1.3.5

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/bin/capture CHANGED
@@ -2235,7 +2235,7 @@ var require_websocket = __commonJS({
2235
2235
  var http = require("http");
2236
2236
  var net = require("net");
2237
2237
  var tls = require("tls");
2238
- var { randomBytes, createHash } = require("crypto");
2238
+ var { randomBytes, createHash: createHash2 } = require("crypto");
2239
2239
  var { Duplex, Readable } = require("stream");
2240
2240
  var { URL: URL2 } = require("url");
2241
2241
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -2895,7 +2895,7 @@ var require_websocket = __commonJS({
2895
2895
  abortHandshake(websocket, socket, "Invalid Upgrade header");
2896
2896
  return;
2897
2897
  }
2898
- const digest = createHash("sha1").update(key + GUID).digest("base64");
2898
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
2899
2899
  if (res.headers["sec-websocket-accept"] !== digest) {
2900
2900
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2901
2901
  return;
@@ -3262,7 +3262,7 @@ var require_websocket_server = __commonJS({
3262
3262
  var EventEmitter = require("events");
3263
3263
  var http = require("http");
3264
3264
  var { Duplex } = require("stream");
3265
- var { createHash } = require("crypto");
3265
+ var { createHash: createHash2 } = require("crypto");
3266
3266
  var extension2 = require_extension();
3267
3267
  var PerMessageDeflate2 = require_permessage_deflate();
3268
3268
  var subprotocol2 = require_subprotocol();
@@ -3563,7 +3563,7 @@ var require_websocket_server = __commonJS({
3563
3563
  );
3564
3564
  }
3565
3565
  if (this._state > RUNNING) return abortHandshake(socket, 503);
3566
- const digest = createHash("sha1").update(key + GUID).digest("base64");
3566
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
3567
3567
  const headers = [
3568
3568
  "HTTP/1.1 101 Switching Protocols",
3569
3569
  "Upgrade: websocket",
@@ -3678,13 +3678,13 @@ var init_client = __esm({
3678
3678
  ready;
3679
3679
  constructor(wsUrl, connectTimeout = 5e3) {
3680
3680
  this.ws = new wrapper_default(wsUrl);
3681
- this.ready = new Promise((resolve3, reject) => {
3681
+ this.ready = new Promise((resolve4, reject) => {
3682
3682
  const timeout = setTimeout(() => {
3683
3683
  reject(new Error(`WebSocket connection timeout (${connectTimeout}ms)`));
3684
3684
  }, connectTimeout);
3685
3685
  this.ws.on("open", () => {
3686
3686
  clearTimeout(timeout);
3687
- resolve3();
3687
+ resolve4();
3688
3688
  });
3689
3689
  this.ws.on("error", (err) => {
3690
3690
  clearTimeout(timeout);
@@ -3720,8 +3720,8 @@ var init_client = __esm({
3720
3720
  async send(method, params = {}, timeout = 6e4) {
3721
3721
  await this.ready;
3722
3722
  const id = ++this.messageId;
3723
- return new Promise((resolve3, reject) => {
3724
- this.pendingRequests.set(id, { resolve: resolve3, reject });
3723
+ return new Promise((resolve4, reject) => {
3724
+ this.pendingRequests.set(id, { resolve: resolve4, reject });
3725
3725
  this.ws.send(JSON.stringify({ id, method, params }));
3726
3726
  const timer = setTimeout(() => {
3727
3727
  if (this.pendingRequests.has(id)) {
@@ -4491,14 +4491,14 @@ async function navigateAndRecord(options) {
4491
4491
  try {
4492
4492
  await Promise.race([
4493
4493
  (async () => {
4494
- await new Promise((resolve3) => {
4494
+ await new Promise((resolve4) => {
4495
4495
  const timer = setTimeout(() => {
4496
4496
  console.error("Page load timeout (10s), continuing with settle...");
4497
- resolve3();
4497
+ resolve4();
4498
4498
  }, 1e4);
4499
4499
  client.on("Page.loadEventFired", () => {
4500
4500
  clearTimeout(timer);
4501
- resolve3();
4501
+ resolve4();
4502
4502
  });
4503
4503
  });
4504
4504
  console.error(`Settling for ${settle}ms...`);
@@ -4539,14 +4539,14 @@ async function navigateAndWait(port, url, options = {}) {
4539
4539
  const client = new CDPClient(tab.webSocketDebuggerUrl);
4540
4540
  await client.waitReady();
4541
4541
  await client.send("Page.enable");
4542
- await new Promise((resolve3, reject) => {
4542
+ await new Promise((resolve4, reject) => {
4543
4543
  const timer = setTimeout(
4544
4544
  () => reject(new Error(`Page load timeout: ${url}`)),
4545
4545
  timeout
4546
4546
  );
4547
4547
  client.on("Page.loadEventFired", () => {
4548
4548
  clearTimeout(timer);
4549
- resolve3();
4549
+ resolve4();
4550
4550
  });
4551
4551
  });
4552
4552
  client.close();
@@ -4800,6 +4800,10 @@ Commands:
4800
4800
  har create Create a HAR recording (returns id)
4801
4801
  har read [id] Read accumulated HAR entries (id optional in active session)
4802
4802
  har delete <id> Delete a HAR recording
4803
+ lib list List available vault libs (dev checkout only)
4804
+ lib search "<q>" Fuzzy-search lib functions [--limit N]
4805
+ lib show <name> Lib + function summaries
4806
+ lib read <name> [fn\u2026] Full input/output schemas (+ .ts source path)
4803
4807
 
4804
4808
  Options:
4805
4809
  --port <port> Override CDP port
@@ -4842,6 +4846,9 @@ Examples:
4842
4846
  capture har create
4843
4847
  capture exec "document.querySelector('form').submit()" --target <id> --har <id>
4844
4848
  capture har read <id>
4849
+ capture lib search "search emails"
4850
+ capture lib read gmail searchEmails
4851
+ capture exec "import {searchEmails} from 'libs/gmail'; const ctx = await getContext(); return await searchEmails({ ...ctx, query: 'invoice' })"
4845
4852
  `;
4846
4853
  }
4847
4854
  });
@@ -4994,11 +5001,11 @@ async function cmdResetTab(parsed, _args) {
4994
5001
  const client = new CDPClient(tab.webSocketDebuggerUrl);
4995
5002
  await client.waitReady();
4996
5003
  await client.send("Page.enable");
4997
- await new Promise((resolve3) => {
4998
- const timer = setTimeout(() => resolve3(), 1e4);
5004
+ await new Promise((resolve4) => {
5005
+ const timer = setTimeout(() => resolve4(), 1e4);
4999
5006
  client.on("Page.loadEventFired", () => {
5000
5007
  clearTimeout(timer);
5001
- resolve3();
5008
+ resolve4();
5002
5009
  });
5003
5010
  });
5004
5011
  client.close();
@@ -5150,6 +5157,173 @@ var init_connection = __esm({
5150
5157
  }
5151
5158
  });
5152
5159
 
5160
+ // src/vault/paths.ts
5161
+ function vaultLibsDir() {
5162
+ const candidates = [
5163
+ path4.resolve(__dirname, "..", "vault", "libs"),
5164
+ // built bin: bin/../vault/libs
5165
+ path4.resolve(__dirname, "..", "..", "vault", "libs")
5166
+ // tsx dev: src/vault/../../vault/libs
5167
+ ];
5168
+ const hit = candidates.find(
5169
+ (p) => fs7.existsSync(path4.join(p, "_runtime", "index.ts"))
5170
+ );
5171
+ if (!hit) throw new Error(DEV_ONLY_MSG);
5172
+ return hit;
5173
+ }
5174
+ function vaultRepoRoot() {
5175
+ return path4.dirname(vaultLibsDir());
5176
+ }
5177
+ function listLibs() {
5178
+ return fs7.readdirSync(vaultLibsDir(), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name).sort();
5179
+ }
5180
+ function unknownLibMsg(name) {
5181
+ return `Unknown lib "${name}". Available: ${listLibs().join(", ")}`;
5182
+ }
5183
+ function isValidLibName(name) {
5184
+ return VALID_LIB_NAME.test(name) && !name.includes("..");
5185
+ }
5186
+ async function loadEsbuild() {
5187
+ return await import("esbuild").catch(() => {
5188
+ throw new Error(DEV_ONLY_MSG);
5189
+ });
5190
+ }
5191
+ var fs7, path4, DEV_ONLY_MSG, VALID_LIB_NAME;
5192
+ var init_paths = __esm({
5193
+ "src/vault/paths.ts"() {
5194
+ fs7 = __toESM(require("fs"));
5195
+ path4 = __toESM(require("path"));
5196
+ DEV_ONLY_MSG = "This is a dev-only feature of capture \u2014 run it from a capture checkout\n(vault/ source + `pnpm install`). Not available in the published package.";
5197
+ VALID_LIB_NAME = /^[A-Za-z0-9_.-]+$/;
5198
+ }
5199
+ });
5200
+
5201
+ // src/vault/bundle.ts
5202
+ function splitLeadingImports(code) {
5203
+ const imports = [];
5204
+ let body = code;
5205
+ for (; ; ) {
5206
+ const m = IMPORT_STMT_RE.exec(body);
5207
+ if (!m) break;
5208
+ imports.push(m[0]);
5209
+ body = body.slice(m[0].length);
5210
+ }
5211
+ return { imports: imports.join(""), body };
5212
+ }
5213
+ function hasImports(code) {
5214
+ return splitLeadingImports(code).imports.length > 0;
5215
+ }
5216
+ function preflightLibs(imports) {
5217
+ const libs = vaultLibsDir();
5218
+ LIB_SPECIFIER_RE.lastIndex = 0;
5219
+ let m;
5220
+ while ((m = LIB_SPECIFIER_RE.exec(imports)) !== null) {
5221
+ const name = m[2];
5222
+ if (!isValidLibName(name) || !fs8.existsSync(path5.join(libs, name, "index.ts"))) {
5223
+ throw new Error(unknownLibMsg(name));
5224
+ }
5225
+ }
5226
+ }
5227
+ function buildEntry(code) {
5228
+ const { imports, body } = splitLeadingImports(code);
5229
+ const expr = body.trim().replace(/;+\s*$/, "");
5230
+ return [
5231
+ imports,
5232
+ `const ${SENTINEL} = (async () => { ${expr} })();`,
5233
+ `${SENTINEL}.then();`
5234
+ ].join("\n");
5235
+ }
5236
+ function vaultResolver() {
5237
+ const libs = vaultLibsDir();
5238
+ const runtime = path5.join(libs, "_runtime", "index.ts");
5239
+ return {
5240
+ name: "vault",
5241
+ setup(build) {
5242
+ build.onResolve({ filter: /^@vallum\/_runtime$/ }, () => ({
5243
+ path: runtime
5244
+ }));
5245
+ build.onResolve({ filter: /^libs\// }, (args) => {
5246
+ const name = args.path.slice("libs/".length);
5247
+ if (!isValidLibName(name)) {
5248
+ return { errors: [{ text: unknownLibMsg(name) }] };
5249
+ }
5250
+ const entry = path5.join(libs, name, "index.ts");
5251
+ return fs8.existsSync(entry) ? { path: entry } : { errors: [{ text: unknownLibMsg(name) }] };
5252
+ });
5253
+ }
5254
+ };
5255
+ }
5256
+ async function runEsbuild(entry) {
5257
+ const esbuild = await loadEsbuild();
5258
+ const opts = {
5259
+ stdin: {
5260
+ contents: entry,
5261
+ resolveDir: vaultRepoRoot(),
5262
+ loader: "ts",
5263
+ sourcefile: "exec.ts"
5264
+ },
5265
+ plugins: [vaultResolver()],
5266
+ bundle: true,
5267
+ write: false,
5268
+ format: "iife",
5269
+ target: "es2020",
5270
+ platform: "browser",
5271
+ treeShaking: true,
5272
+ minify: false,
5273
+ logLevel: "silent"
5274
+ };
5275
+ try {
5276
+ return await esbuild.build(opts);
5277
+ } catch (error) {
5278
+ if (error instanceof Error && error.message.includes("service is no longer running")) {
5279
+ esbuild.stop();
5280
+ return await esbuild.build(opts);
5281
+ }
5282
+ throw error;
5283
+ }
5284
+ }
5285
+ function applyReturnInsert(bundled) {
5286
+ const anchor = `${SENTINEL}.then(`;
5287
+ const i = bundled.lastIndexOf(anchor);
5288
+ return i === -1 ? bundled : bundled.slice(0, i) + "return " + bundled.slice(i);
5289
+ }
5290
+ function formatBuildErrors(errors) {
5291
+ return errors.map((e) => {
5292
+ const l = e.location;
5293
+ const loc = l ? ` (${l.file}:${l.line}:${l.column})` : "";
5294
+ return `${e.text}${loc}`;
5295
+ }).join("\n");
5296
+ }
5297
+ async function bundleExec(code) {
5298
+ preflightLibs(splitLeadingImports(code).imports);
5299
+ const entry = buildEntry(code);
5300
+ let result;
5301
+ try {
5302
+ result = await runEsbuild(entry);
5303
+ } catch (error) {
5304
+ const errs = error?.errors;
5305
+ if (Array.isArray(errs) && errs.length > 0) {
5306
+ throw new Error(formatBuildErrors(errs));
5307
+ }
5308
+ throw error;
5309
+ }
5310
+ if (!result.outputFiles || result.outputFiles.length === 0) {
5311
+ throw new Error("esbuild produced no output");
5312
+ }
5313
+ return applyReturnInsert(result.outputFiles[0].text);
5314
+ }
5315
+ var fs8, path5, SENTINEL, IMPORT_STMT_RE, LIB_SPECIFIER_RE;
5316
+ var init_bundle = __esm({
5317
+ "src/vault/bundle.ts"() {
5318
+ fs8 = __toESM(require("fs"));
5319
+ path5 = __toESM(require("path"));
5320
+ init_paths();
5321
+ SENTINEL = "__CAPTURE_RESULT";
5322
+ IMPORT_STMT_RE = /^\s*(?:(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)\s*)*import\b(?!\s*\()[^'"`]*?(['"])(?:[^'"`\\]|\\.)*\1[^\n;]*;?/;
5323
+ LIB_SPECIFIER_RE = /(['"])libs\/((?:[^'"\\]|\\.)*)\1/g;
5324
+ }
5325
+ });
5326
+
5153
5327
  // src/cdp/commands/exec.ts
5154
5328
  async function cmdExec(parsed, _args) {
5155
5329
  if (parsed.help) {
@@ -5162,13 +5336,16 @@ Examples:
5162
5336
  capture exec "document.title" --target <id>
5163
5337
  capture exec "await fetch('/api/data').then(r=>r.json())" --target <id>
5164
5338
  capture exec "document.querySelector('.btn').click()" --target <id>
5165
- capture exec --file /tmp/scrape.js --target <id> --record`
5339
+ capture exec --file /tmp/scrape.js --target <id> --record
5340
+
5341
+ Import vault libs (dev checkout only): static imports first, then your code.
5342
+ capture exec "import {searchEmails} from 'libs/gmail'; const ctx = await getContext(); return await searchEmails({ ...ctx, query: 'invoice' })"`
5166
5343
  );
5167
5344
  process.exit(0);
5168
5345
  }
5169
5346
  let code;
5170
5347
  if (parsed.file) {
5171
- code = fs7.readFileSync(parsed.file, "utf-8");
5348
+ code = fs9.readFileSync(parsed.file, "utf-8");
5172
5349
  } else {
5173
5350
  code = parsed.positional[0];
5174
5351
  if (!code) {
@@ -5185,6 +5362,15 @@ Examples:
5185
5362
  process.exit(1);
5186
5363
  }
5187
5364
  }
5365
+ let prebuilt;
5366
+ if (hasImports(code)) {
5367
+ try {
5368
+ prebuilt = await bundleExec(code);
5369
+ } catch (e) {
5370
+ console.error(`ERROR: ${e.message}`);
5371
+ process.exit(1);
5372
+ }
5373
+ }
5188
5374
  await withConnection(
5189
5375
  parsed,
5190
5376
  async (client) => {
@@ -5194,8 +5380,7 @@ Examples:
5194
5380
  standaloneRecorder = new HARRecorder(client);
5195
5381
  await standaloneRecorder.start();
5196
5382
  }
5197
- const needsAsyncWrap = /\bawait\b/.test(code);
5198
- const expression = needsAsyncWrap ? `(async () => { ${code} })()` : code;
5383
+ const expression = prebuilt ? prebuilt : /\bawait\b/.test(code) ? `(async () => { ${code} })()` : code;
5199
5384
  const evalResult = await client.send("Runtime.evaluate", {
5200
5385
  expression,
5201
5386
  awaitPromise: true,
@@ -5217,13 +5402,14 @@ Examples:
5217
5402
  );
5218
5403
  return;
5219
5404
  }
5220
- var fs7;
5405
+ var fs9;
5221
5406
  var init_exec = __esm({
5222
5407
  "src/cdp/commands/exec.ts"() {
5223
- fs7 = __toESM(require("fs"));
5408
+ fs9 = __toESM(require("fs"));
5224
5409
  init_connection();
5225
5410
  init_har_recorder();
5226
5411
  init_har_output();
5412
+ init_bundle();
5227
5413
  }
5228
5414
  });
5229
5415
 
@@ -5330,7 +5516,7 @@ async function cmdScreenshot(parsed, _args) {
5330
5516
  async (client) => {
5331
5517
  const png = await captureScreenshot(client, viewport, { fullPage: parsed.fullPage });
5332
5518
  const outPath = parsed.out ? parsed.out : nextStepPath("screenshot", "manual") ?? `/tmp/capture-screenshot-${Date.now()}.png`;
5333
- fs8.writeFileSync(outPath, png);
5519
+ fs10.writeFileSync(outPath, png);
5334
5520
  return { path: outPath, bytes: png.length };
5335
5521
  },
5336
5522
  { settle: 0 }
@@ -5436,10 +5622,10 @@ Consider adding role and aria-label attributes to interactive elements in your f
5436
5622
  }
5437
5623
  return;
5438
5624
  }
5439
- var fs8;
5625
+ var fs10;
5440
5626
  var init_ui = __esm({
5441
5627
  "src/cdp/commands/ui.ts"() {
5442
- fs8 = __toESM(require("fs"));
5628
+ fs10 = __toESM(require("fs"));
5443
5629
  init_connection();
5444
5630
  init_screenshot();
5445
5631
  init_a11y();
@@ -5668,6 +5854,400 @@ var init_har = __esm({
5668
5854
  }
5669
5855
  });
5670
5856
 
5857
+ // src/vault/docs.ts
5858
+ function isAgentVisible(doc) {
5859
+ return AGENT_VISIBLE_VISIBILITIES.has(doc.visibility);
5860
+ }
5861
+ function cacheDir() {
5862
+ return path6.join(vaultRepoRoot(), ".cache", "docs");
5863
+ }
5864
+ function schemasPathFor(svc) {
5865
+ return path6.join(vaultLibsDir(), svc, "schemas.ts");
5866
+ }
5867
+ function srcSha(svc) {
5868
+ return (0, import_crypto.createHash)("sha256").update(fs11.readFileSync(schemasPathFor(svc), "utf8")).digest("hex");
5869
+ }
5870
+ function toJsonSchema(zodSchema) {
5871
+ const s = zodSchema;
5872
+ if (s?._zod?.def?.type === "void") {
5873
+ const desc = s.description;
5874
+ return desc ? { type: "null", description: desc } : { type: "null" };
5875
+ }
5876
+ if (!s?.toJSONSchema) {
5877
+ throw new Error(
5878
+ "Schema missing toJSONSchema method - ensure Zod 4+ is installed"
5879
+ );
5880
+ }
5881
+ return s.toJSONSchema();
5882
+ }
5883
+ async function buildDoc(svc) {
5884
+ const esbuild = await loadEsbuild();
5885
+ const result = await esbuild.build({
5886
+ entryPoints: [schemasPathFor(svc)],
5887
+ bundle: true,
5888
+ write: false,
5889
+ format: "cjs",
5890
+ platform: "node",
5891
+ target: "es2020",
5892
+ logLevel: "silent"
5893
+ });
5894
+ const text = result.outputFiles?.[0]?.text;
5895
+ if (!text) throw new Error(`esbuild produced no output for ${svc}/schemas.ts`);
5896
+ const mod = { exports: {} };
5897
+ new Function("module", "exports", "require", text)(
5898
+ mod,
5899
+ mod.exports,
5900
+ cjsRequire
5901
+ );
5902
+ const x = mod.exports;
5903
+ const allSchemas = x.allSchemas ?? [];
5904
+ const doc = {
5905
+ library: `@vallum/${svc}`,
5906
+ description: x.libraryDescription ?? `${svc} service library`,
5907
+ visibility: x.libraryVisibility ?? "public",
5908
+ functions: allSchemas.map((schema) => ({
5909
+ name: schema.name,
5910
+ description: schema.description,
5911
+ ...schema.notes ? { notes: schema.notes } : {},
5912
+ input: toJsonSchema(schema.input),
5913
+ output: toJsonSchema(schema.output)
5914
+ }))
5915
+ };
5916
+ if (x.libraryNotes) doc.notes = x.libraryNotes;
5917
+ if (x.loginUrl) doc.loginUrl = x.loginUrl;
5918
+ return doc;
5919
+ }
5920
+ async function getDoc(name) {
5921
+ if (!isValidLibName(name) || !fs11.existsSync(schemasPathFor(name))) {
5922
+ throw new Error(unknownLibMsg(name));
5923
+ }
5924
+ const sha = srcSha(name);
5925
+ const cacheFile = path6.join(cacheDir(), `${name}.json`);
5926
+ if (fs11.existsSync(cacheFile)) {
5927
+ try {
5928
+ const cached = JSON.parse(fs11.readFileSync(cacheFile, "utf8"));
5929
+ if (cached._srcSha === sha) {
5930
+ delete cached._srcSha;
5931
+ return cached;
5932
+ }
5933
+ } catch {
5934
+ }
5935
+ }
5936
+ const doc = await buildDoc(name);
5937
+ fs11.mkdirSync(cacheDir(), { recursive: true });
5938
+ fs11.writeFileSync(
5939
+ cacheFile,
5940
+ JSON.stringify({ ...doc, _srcSha: sha }, null, 2)
5941
+ );
5942
+ return doc;
5943
+ }
5944
+ async function listDocs() {
5945
+ const out = [];
5946
+ for (const name of listLibs()) {
5947
+ let doc;
5948
+ try {
5949
+ doc = await getDoc(name);
5950
+ } catch (e) {
5951
+ console.error(
5952
+ `WARN: skipping lib "${name}" \u2014 ${e.message}`
5953
+ );
5954
+ continue;
5955
+ }
5956
+ if (isAgentVisible(doc)) out.push(doc);
5957
+ }
5958
+ return out;
5959
+ }
5960
+ var fs11, path6, import_crypto, AGENT_VISIBLE_VISIBILITIES, cjsRequire;
5961
+ var init_docs = __esm({
5962
+ "src/vault/docs.ts"() {
5963
+ fs11 = __toESM(require("fs"));
5964
+ path6 = __toESM(require("path"));
5965
+ import_crypto = require("crypto");
5966
+ init_paths();
5967
+ AGENT_VISIBLE_VISIBILITIES = /* @__PURE__ */ new Set(["public", "chat"]);
5968
+ cjsRequire = (id) => {
5969
+ if (typeof require === "function") return require(id);
5970
+ throw new Error(`cannot require('${id}') \u2014 unavailable in this runtime`);
5971
+ };
5972
+ }
5973
+ });
5974
+
5975
+ // src/cdp/commands/lib.ts
5976
+ function scoreToken(token, service, fnName, fnDesc, serviceDesc) {
5977
+ const q = token.toLowerCase();
5978
+ const fn = fnName.toLowerCase();
5979
+ const svc = service.toLowerCase();
5980
+ const fnD = fnDesc.toLowerCase();
5981
+ const svcD = serviceDesc.toLowerCase();
5982
+ let score = 0;
5983
+ let match = "";
5984
+ if (fn === q) {
5985
+ score += FUNCTION_NAME_EXACT;
5986
+ match = "function-name (exact)";
5987
+ } else if (fn.startsWith(q)) {
5988
+ score += FUNCTION_NAME_PREFIX;
5989
+ match = "function-name (prefix)";
5990
+ } else if (fn.includes(q)) {
5991
+ score += FUNCTION_NAME_SUBSTRING;
5992
+ match = "function-name";
5993
+ }
5994
+ if (svc.includes(q)) {
5995
+ score += SERVICE_NAME_MATCH;
5996
+ if (!match) match = "service-name";
5997
+ }
5998
+ if (fnD.includes(q)) {
5999
+ score += FUNCTION_DESC_SUBSTRING;
6000
+ if (!match) match = "function-desc";
6001
+ }
6002
+ if (svcD.includes(q)) {
6003
+ score += SERVICE_DESC_SUBSTRING;
6004
+ if (!match) match = "service-desc";
6005
+ }
6006
+ return { score, match };
6007
+ }
6008
+ function scoreHit(query, service, fnName, fnDesc, serviceDesc) {
6009
+ const tokens = query.split(/\s+/).filter(Boolean);
6010
+ if (tokens.length === 0) return { score: 0, match: "" };
6011
+ if (tokens.length === 1) {
6012
+ return scoreToken(tokens[0], service, fnName, fnDesc, serviceDesc);
6013
+ }
6014
+ let total = 0;
6015
+ let bestScore = -1;
6016
+ let bestMatch = "";
6017
+ for (const token of tokens) {
6018
+ const r = scoreToken(token, service, fnName, fnDesc, serviceDesc);
6019
+ if (r.score === 0) return { score: 0, match: "" };
6020
+ total += r.score;
6021
+ if (r.score > bestScore) {
6022
+ bestScore = r.score;
6023
+ bestMatch = r.match;
6024
+ }
6025
+ }
6026
+ return { score: total, match: bestMatch };
6027
+ }
6028
+ function resolveName(query, names) {
6029
+ if (names.includes(query)) return { name: query };
6030
+ const lower = query.toLowerCase();
6031
+ const prefix = names.filter((n) => n.toLowerCase().startsWith(lower));
6032
+ if (prefix.length === 1) return { name: prefix[0] };
6033
+ if (prefix.length > 1) {
6034
+ return { error: `Ambiguous lib "${query}" \u2014 matches: ${prefix.join(", ")}` };
6035
+ }
6036
+ return { error: `Unknown lib "${query}". Available: ${names.join(", ")}` };
6037
+ }
6038
+ async function cmdLib(parsed, _args) {
6039
+ if (parsed.help) {
6040
+ console.log(USAGE);
6041
+ process.exit(0);
6042
+ }
6043
+ const sub = parsed.positional[0];
6044
+ if (sub === "list") {
6045
+ const docs = await listDocs();
6046
+ const rows = docs.map((d) => {
6047
+ const name = svcName(d);
6048
+ return {
6049
+ name,
6050
+ functionCount: d.functions.length,
6051
+ description: d.description,
6052
+ srcPath: srcPathFor(name)
6053
+ };
6054
+ });
6055
+ console.log(JSON.stringify(rows, null, 2));
6056
+ console.error(
6057
+ `
6058
+ ${rows.length} libs. Next: capture lib show <name> \xB7 capture lib read <name> <fn>`
6059
+ );
6060
+ return;
6061
+ }
6062
+ if (sub === "search") {
6063
+ const query = parsed.positional[1];
6064
+ if (!query) {
6065
+ console.error('Usage: capture lib search "<query>" [--limit N]');
6066
+ process.exit(1);
6067
+ }
6068
+ const limit = Math.min(
6069
+ typeof parsed.limit === "number" && parsed.limit > 0 ? parsed.limit : 20,
6070
+ 500
6071
+ );
6072
+ const docs = await listDocs();
6073
+ const hits = [];
6074
+ for (const d of docs) {
6075
+ const service = svcName(d);
6076
+ for (const fn of d.functions) {
6077
+ const { score, match } = scoreHit(
6078
+ query,
6079
+ service,
6080
+ fn.name,
6081
+ fn.description ?? "",
6082
+ d.description ?? ""
6083
+ );
6084
+ if (score > 0) {
6085
+ hits.push({
6086
+ service,
6087
+ function: fn.name,
6088
+ match,
6089
+ score,
6090
+ description: fn.description,
6091
+ srcPath: srcPathFor(service)
6092
+ });
6093
+ }
6094
+ }
6095
+ }
6096
+ hits.sort((a, b) => b.score - a.score);
6097
+ const top = hits.slice(0, limit);
6098
+ console.log(JSON.stringify(top, null, 2));
6099
+ console.error(
6100
+ `
6101
+ ${hits.length} hit(s)${hits.length > limit ? ` (showing ${limit})` : ""}. Next: capture lib read <service> <function>`
6102
+ );
6103
+ return;
6104
+ }
6105
+ if (sub === "show") {
6106
+ const name = parsed.positional[1];
6107
+ if (!name) {
6108
+ console.error("Usage: capture lib show <name>");
6109
+ process.exit(1);
6110
+ }
6111
+ const r = resolveName(name, listLibs());
6112
+ if ("error" in r) {
6113
+ console.error(r.error);
6114
+ process.exit(1);
6115
+ }
6116
+ let doc;
6117
+ try {
6118
+ doc = await getDoc(r.name);
6119
+ } catch (e) {
6120
+ console.error(e.message);
6121
+ process.exit(1);
6122
+ }
6123
+ if (!isAgentVisible(doc)) {
6124
+ console.error(
6125
+ `Unknown lib "${name}". Available: ${listLibs().join(", ")}`
6126
+ );
6127
+ process.exit(1);
6128
+ }
6129
+ const service = r.name;
6130
+ const out = {
6131
+ service,
6132
+ description: doc.description,
6133
+ ...doc.loginUrl ? { loginUrl: doc.loginUrl } : {},
6134
+ ...doc.notes ? { notes: doc.notes } : {},
6135
+ srcPath: srcPathFor(service),
6136
+ functions: doc.functions.map((f) => ({
6137
+ name: f.name,
6138
+ description: f.description
6139
+ }))
6140
+ };
6141
+ console.log(JSON.stringify(out, null, 2));
6142
+ console.error(
6143
+ `
6144
+ Schemas required before exec \u2014 capture lib read ${service} <fn>`
6145
+ );
6146
+ return;
6147
+ }
6148
+ if (sub === "read") {
6149
+ const name = parsed.positional[1];
6150
+ if (!name) {
6151
+ console.error("Usage: capture lib read <name> [fn\u2026]");
6152
+ process.exit(1);
6153
+ }
6154
+ const r = resolveName(name, listLibs());
6155
+ if ("error" in r) {
6156
+ console.error(r.error);
6157
+ process.exit(1);
6158
+ }
6159
+ let doc;
6160
+ try {
6161
+ doc = await getDoc(r.name);
6162
+ } catch (e) {
6163
+ console.error(e.message);
6164
+ process.exit(1);
6165
+ }
6166
+ const service = svcName(doc);
6167
+ const fns = parsed.positional.slice(2);
6168
+ if (fns.length === 0) {
6169
+ const out2 = {
6170
+ service,
6171
+ description: doc.description,
6172
+ ...doc.notes ? { notes: doc.notes } : {},
6173
+ srcPath: srcPathFor(service),
6174
+ functions: doc.functions.map((f) => ({
6175
+ name: f.name,
6176
+ description: f.description
6177
+ }))
6178
+ };
6179
+ console.log(JSON.stringify(out2, null, 2));
6180
+ console.error(
6181
+ `
6182
+ Name functions to load schemas: capture lib read ${service} <fn> [fn\u2026]`
6183
+ );
6184
+ return;
6185
+ }
6186
+ const byName = new Map(doc.functions.map((f) => [f.name, f]));
6187
+ const found = [];
6188
+ const missing = [];
6189
+ for (const op of fns) {
6190
+ const fn = byName.get(op);
6191
+ if (fn) found.push(fn);
6192
+ else missing.push(op);
6193
+ }
6194
+ if (found.length === 0) {
6195
+ const available = doc.functions.map((f) => f.name).join(", ");
6196
+ const label = missing.length > 1 ? `Functions "${missing.join('", "')}"` : `Function "${missing[0]}"`;
6197
+ console.error(`${label} not in ${service}. Available: ${available}`);
6198
+ process.exit(1);
6199
+ }
6200
+ const out = {
6201
+ service,
6202
+ srcPath: srcPathFor(service),
6203
+ functions: found.map((f) => ({
6204
+ name: f.name,
6205
+ description: f.description,
6206
+ ...f.notes ? { notes: f.notes } : {},
6207
+ input: f.input,
6208
+ output: f.output
6209
+ })),
6210
+ ...missing.length > 0 ? { missing } : {}
6211
+ };
6212
+ console.log(JSON.stringify(out, null, 2));
6213
+ const firstFn = found[0].name;
6214
+ console.error(
6215
+ `
6216
+ Run it: capture exec "import {${firstFn}} from 'libs/${service}'; return await ${firstFn}({\u2026})"`
6217
+ );
6218
+ return;
6219
+ }
6220
+ console.error(USAGE);
6221
+ process.exit(1);
6222
+ }
6223
+ var path7, USAGE, FUNCTION_NAME_EXACT, FUNCTION_NAME_PREFIX, FUNCTION_NAME_SUBSTRING, SERVICE_NAME_MATCH, FUNCTION_DESC_SUBSTRING, SERVICE_DESC_SUBSTRING, svcName, srcPathFor;
6224
+ var init_lib = __esm({
6225
+ "src/cdp/commands/lib.ts"() {
6226
+ path7 = __toESM(require("path"));
6227
+ init_paths();
6228
+ init_docs();
6229
+ USAGE = `Usage: capture lib <list|search|show|read> [options]
6230
+
6231
+ list List available libs (name, fn count, description, srcPath)
6232
+ search "<query>" [--limit N] Fuzzy-search functions across all libs (ranked)
6233
+ show <name> Lib + function summaries (no schemas)
6234
+ read <name> [fn\u2026] Full input/output JSON schemas (+ .ts source path)
6235
+
6236
+ Then run a lib in the active authenticated tab:
6237
+ capture exec "import {<fn>} from 'libs/<name>'; return await <fn>({\u2026})"
6238
+
6239
+ Dev-checkout-only feature (requires vault/ source + esbuild).`;
6240
+ FUNCTION_NAME_EXACT = 100;
6241
+ FUNCTION_NAME_PREFIX = 60;
6242
+ FUNCTION_NAME_SUBSTRING = 40;
6243
+ SERVICE_NAME_MATCH = 50;
6244
+ FUNCTION_DESC_SUBSTRING = 20;
6245
+ SERVICE_DESC_SUBSTRING = 10;
6246
+ svcName = (doc) => doc.library.replace(/^@vallum\//, "");
6247
+ srcPathFor = (name) => path7.join(vaultLibsDir(), name, "index.ts");
6248
+ }
6249
+ });
6250
+
5671
6251
  // src/cdp/dispatch.ts
5672
6252
  async function cdpMain2() {
5673
6253
  const args = process.argv.slice(2);
@@ -5699,6 +6279,8 @@ async function cdpMain2() {
5699
6279
  return cmdNetwork(parsed, args);
5700
6280
  case "har":
5701
6281
  return cmdHar(parsed, args);
6282
+ case "lib":
6283
+ return cmdLib(parsed, args);
5702
6284
  default:
5703
6285
  console.log(CDP_USAGE);
5704
6286
  }
@@ -5712,6 +6294,7 @@ var init_dispatch = __esm({
5712
6294
  init_ui();
5713
6295
  init_traffic();
5714
6296
  init_har();
6297
+ init_lib();
5715
6298
  }
5716
6299
  });
5717
6300
 
@@ -5769,30 +6352,30 @@ var init_cdp = __esm({
5769
6352
  });
5770
6353
 
5771
6354
  // src/capture.ts
5772
- var fs10 = __toESM(require("fs"));
5773
- var path5 = __toESM(require("path"));
6355
+ var fs13 = __toESM(require("fs"));
6356
+ var path9 = __toESM(require("path"));
5774
6357
  init_cdp();
5775
6358
 
5776
6359
  // src/session/commands.ts
5777
- var fs9 = __toESM(require("fs"));
5778
- var path4 = __toESM(require("path"));
6360
+ var fs12 = __toESM(require("fs"));
6361
+ var path8 = __toESM(require("path"));
5779
6362
  var os3 = __toESM(require("os"));
5780
6363
  var import_child_process2 = require("child_process");
5781
6364
  init_har_manager();
5782
6365
  init_session_context();
5783
- var CAPTURE_ROOT2 = path4.join(os3.tmpdir(), "capture-sessions");
6366
+ var CAPTURE_ROOT2 = path8.join(os3.tmpdir(), "capture-sessions");
5784
6367
  function sessionDir(id) {
5785
- return path4.join(CAPTURE_ROOT2, id);
6368
+ return path8.join(CAPTURE_ROOT2, id);
5786
6369
  }
5787
6370
  function sessionMetaPath(id) {
5788
- return path4.join(sessionDir(id), ".session.json");
6371
+ return path8.join(sessionDir(id), ".session.json");
5789
6372
  }
5790
6373
  function readSession(id) {
5791
6374
  const metaPath = sessionMetaPath(id);
5792
- if (!fs9.existsSync(metaPath)) {
6375
+ if (!fs12.existsSync(metaPath)) {
5793
6376
  throw new Error(`No capture session found: ${id}`);
5794
6377
  }
5795
- return JSON.parse(fs9.readFileSync(metaPath, "utf-8"));
6378
+ return JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
5796
6379
  }
5797
6380
  function generateId() {
5798
6381
  return `cap-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
@@ -5806,8 +6389,8 @@ async function start(args) {
5806
6389
  }
5807
6390
  const id = generateId();
5808
6391
  const dir = sessionDir(id);
5809
- fs9.mkdirSync(path4.join(dir, "shots"), { recursive: true });
5810
- fs9.mkdirSync(path4.join(dir, "a11y"), { recursive: true });
6392
+ fs12.mkdirSync(path8.join(dir, "shots"), { recursive: true });
6393
+ fs12.mkdirSync(path8.join(dir, "a11y"), { recursive: true });
5811
6394
  let harId = null;
5812
6395
  try {
5813
6396
  const harResult = createHarRecording();
@@ -5836,7 +6419,7 @@ async function start(args) {
5836
6419
  stepCount: 0,
5837
6420
  logPids: []
5838
6421
  };
5839
- fs9.writeFileSync(sessionMetaPath(id), JSON.stringify(session, null, 2));
6422
+ fs12.writeFileSync(sessionMetaPath(id), JSON.stringify(session, null, 2));
5840
6423
  setActiveSession({
5841
6424
  sessionId: id,
5842
6425
  dir,
@@ -5849,8 +6432,8 @@ async function start(args) {
5849
6432
  bundleDir: dir,
5850
6433
  harId,
5851
6434
  targetId,
5852
- shotsDir: path4.join(dir, "shots"),
5853
- a11yDir: path4.join(dir, "a11y")
6435
+ shotsDir: path8.join(dir, "shots"),
6436
+ a11yDir: path8.join(dir, "a11y")
5854
6437
  };
5855
6438
  console.log(JSON.stringify(result, null, 2));
5856
6439
  console.error(`
@@ -5867,8 +6450,8 @@ function logCommand(args) {
5867
6450
  console.error("Usage: capture log <path> [--name label] [--session <id>]");
5868
6451
  process.exit(1);
5869
6452
  }
5870
- const resolved = path4.resolve(sourcePath);
5871
- if (!fs9.existsSync(resolved)) {
6453
+ const resolved = path8.resolve(sourcePath);
6454
+ if (!fs12.existsSync(resolved)) {
5872
6455
  throw new Error(`Log file not found: ${resolved}`);
5873
6456
  }
5874
6457
  let name = null;
@@ -5885,21 +6468,21 @@ function logCommand(args) {
5885
6468
  sessionId = active.sessionId;
5886
6469
  }
5887
6470
  const session = readSession(sessionId);
5888
- name = name ?? path4.basename(resolved, path4.extname(resolved));
5889
- const logsDir = path4.join(session.dir, "logs");
5890
- fs9.mkdirSync(logsDir, { recursive: true });
5891
- const destPath = path4.join(logsDir, `${name}.log`);
5892
- const outFd = fs9.openSync(destPath, "a");
6471
+ name = name ?? path8.basename(resolved, path8.extname(resolved));
6472
+ const logsDir = path8.join(session.dir, "logs");
6473
+ fs12.mkdirSync(logsDir, { recursive: true });
6474
+ const destPath = path8.join(logsDir, `${name}.log`);
6475
+ const outFd = fs12.openSync(destPath, "a");
5893
6476
  const child = (0, import_child_process2.spawn)(
5894
6477
  "sh",
5895
6478
  ["-c", `tail -f "${resolved}" | perl -MPOSIX -ne 'print strftime("%Y-%m-%dT%H:%M:%SZ",gmtime())." ".$_'`],
5896
6479
  { detached: true, stdio: ["ignore", outFd, "ignore"] }
5897
6480
  );
5898
6481
  child.unref();
5899
- fs9.closeSync(outFd);
6482
+ fs12.closeSync(outFd);
5900
6483
  const pid = child.pid;
5901
6484
  session.logPids.push({ pid, name, sourcePath: resolved });
5902
- fs9.writeFileSync(sessionMetaPath(session.id), JSON.stringify(session, null, 2));
6485
+ fs12.writeFileSync(sessionMetaPath(session.id), JSON.stringify(session, null, 2));
5903
6486
  console.log(JSON.stringify({ name, sourcePath: resolved, destPath, pid }, null, 2));
5904
6487
  }
5905
6488
  async function stop(args) {
@@ -5921,17 +6504,17 @@ async function stop(args) {
5921
6504
  if (session.logPids?.length) {
5922
6505
  await new Promise((r) => setTimeout(r, 200));
5923
6506
  }
5924
- const shotsDir = path4.join(session.dir, "shots");
5925
- const screenshots = fs9.existsSync(shotsDir) ? fs9.readdirSync(shotsDir).filter((f) => f.endsWith(".png") || f.endsWith(".jpg")).map((f) => ({ name: f, path: path4.join(shotsDir, f) })) : [];
5926
- const a11yDir = path4.join(session.dir, "a11y");
5927
- const a11y = fs9.existsSync(a11yDir) ? fs9.readdirSync(a11yDir).filter((f) => f.endsWith(".json") || f.endsWith(".txt")).map((f) => ({ name: f, path: path4.join(a11yDir, f) })) : [];
6507
+ const shotsDir = path8.join(session.dir, "shots");
6508
+ const screenshots = fs12.existsSync(shotsDir) ? fs12.readdirSync(shotsDir).filter((f) => f.endsWith(".png") || f.endsWith(".jpg")).map((f) => ({ name: f, path: path8.join(shotsDir, f) })) : [];
6509
+ const a11yDir = path8.join(session.dir, "a11y");
6510
+ const a11y = fs12.existsSync(a11yDir) ? fs12.readdirSync(a11yDir).filter((f) => f.endsWith(".json") || f.endsWith(".txt")).map((f) => ({ name: f, path: path8.join(a11yDir, f) })) : [];
5928
6511
  let har = null;
5929
6512
  if (session.harId) {
5930
6513
  try {
5931
6514
  const harData = readHarRecording(session.harId);
5932
6515
  if (harData) {
5933
- const harPath = path4.join(session.dir, "har.json");
5934
- fs9.writeFileSync(harPath, JSON.stringify(harData, null, 2));
6516
+ const harPath = path8.join(session.dir, "har.json");
6517
+ fs12.writeFileSync(harPath, JSON.stringify(harData, null, 2));
5935
6518
  har = { id: session.harId, path: harPath, entryCount: harData.log.entries.length };
5936
6519
  try {
5937
6520
  deleteHarRecording(session.harId);
@@ -5942,16 +6525,16 @@ async function stop(args) {
5942
6525
  console.error(`Warning: could not read HAR: ${err instanceof Error ? err.message : err}`);
5943
6526
  }
5944
6527
  }
5945
- const logsDir = path4.join(session.dir, "logs");
5946
- const logs = fs9.existsSync(logsDir) ? fs9.readdirSync(logsDir).filter((f) => f.endsWith(".log")).map((f) => {
5947
- const filePath = path4.join(logsDir, f);
5948
- const content = fs9.readFileSync(filePath, "utf-8");
6528
+ const logsDir = path8.join(session.dir, "logs");
6529
+ const logs = fs12.existsSync(logsDir) ? fs12.readdirSync(logsDir).filter((f) => f.endsWith(".log")).map((f) => {
6530
+ const filePath = path8.join(logsDir, f);
6531
+ const content = fs12.readFileSync(filePath, "utf-8");
5949
6532
  const lines = content ? content.split("\n").filter(Boolean).length : 0;
5950
6533
  return { name: f, path: filePath, lines };
5951
6534
  }) : [];
5952
6535
  const knownDirs = /* @__PURE__ */ new Set(["shots", "a11y", "logs"]);
5953
6536
  const knownFiles = /* @__PURE__ */ new Set([".session.json", "har.json", "bundle.json"]);
5954
- const other = fs9.readdirSync(session.dir).filter((f) => !knownDirs.has(f) && !knownFiles.has(f)).map((f) => ({ name: f, path: path4.join(session.dir, f) }));
6537
+ const other = fs12.readdirSync(session.dir).filter((f) => !knownDirs.has(f) && !knownFiles.has(f)).map((f) => ({ name: f, path: path8.join(session.dir, f) }));
5955
6538
  clearActiveSession();
5956
6539
  const manifest = {
5957
6540
  id: session.id,
@@ -5965,8 +6548,8 @@ async function stop(args) {
5965
6548
  logs,
5966
6549
  other
5967
6550
  };
5968
- const bundlePath = path4.join(session.dir, "bundle.json");
5969
- fs9.writeFileSync(bundlePath, JSON.stringify(manifest, null, 2));
6551
+ const bundlePath = path8.join(session.dir, "bundle.json");
6552
+ fs12.writeFileSync(bundlePath, JSON.stringify(manifest, null, 2));
5970
6553
  console.log(JSON.stringify({
5971
6554
  bundlePath,
5972
6555
  summary: {
@@ -5983,13 +6566,13 @@ Bundle written: ${bundlePath}`);
5983
6566
  console.error(`Read it: capture session view ${id}`);
5984
6567
  }
5985
6568
  function list() {
5986
- if (!fs9.existsSync(CAPTURE_ROOT2)) {
6569
+ if (!fs12.existsSync(CAPTURE_ROOT2)) {
5987
6570
  console.log("[]");
5988
6571
  return;
5989
6572
  }
5990
- const sessions = fs9.readdirSync(CAPTURE_ROOT2).filter((d) => fs9.existsSync(sessionMetaPath(d))).map((d) => {
6573
+ const sessions = fs12.readdirSync(CAPTURE_ROOT2).filter((d) => fs12.existsSync(sessionMetaPath(d))).map((d) => {
5991
6574
  const session = readSession(d);
5992
- const hasBundled = fs9.existsSync(path4.join(session.dir, "bundle.json"));
6575
+ const hasBundled = fs12.existsSync(path8.join(session.dir, "bundle.json"));
5993
6576
  return { id: session.id, startedAt: session.startedAt, url: session.url, status: hasBundled ? "stopped" : "active" };
5994
6577
  });
5995
6578
  console.log(JSON.stringify(sessions, null, 2));
@@ -6001,12 +6584,12 @@ function view(args) {
6001
6584
  process.exit(1);
6002
6585
  }
6003
6586
  const session = readSession(id);
6004
- const bundlePath = path4.join(session.dir, "bundle.json");
6005
- if (!fs9.existsSync(bundlePath)) {
6587
+ const bundlePath = path8.join(session.dir, "bundle.json");
6588
+ if (!fs12.existsSync(bundlePath)) {
6006
6589
  console.error(`Session ${id} hasn't been stopped yet. Run: capture session stop ${id}`);
6007
6590
  process.exit(1);
6008
6591
  }
6009
- const manifest = JSON.parse(fs9.readFileSync(bundlePath, "utf-8"));
6592
+ const manifest = JSON.parse(fs12.readFileSync(bundlePath, "utf-8"));
6010
6593
  const filter = args.find((_, i) => args[i - 1] === "--filter");
6011
6594
  if (filter) {
6012
6595
  const section = manifest[filter];
@@ -6059,14 +6642,14 @@ See also: capture --help Full command list`);
6059
6642
  async function main() {
6060
6643
  const [command, ...args] = process.argv.slice(2);
6061
6644
  if (command === "--version" || command === "-v" || command === "version") {
6062
- const declared = "1.3.4";
6645
+ const declared = "1.3.5";
6063
6646
  if (declared) {
6064
6647
  console.log(declared);
6065
6648
  return;
6066
6649
  }
6067
6650
  try {
6068
- const pkgPath = path5.resolve(__dirname, "..", "package.json");
6069
- const pkg = JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
6651
+ const pkgPath = path9.resolve(__dirname, "..", "package.json");
6652
+ const pkg = JSON.parse(fs13.readFileSync(pkgPath, "utf-8"));
6070
6653
  console.log(pkg.version);
6071
6654
  } catch {
6072
6655
  console.log("unknown");
@@ -6090,6 +6673,7 @@ async function main() {
6090
6673
  case "record":
6091
6674
  case "navigate":
6092
6675
  case "har":
6676
+ case "lib":
6093
6677
  case "network":
6094
6678
  case "list":
6095
6679
  return cdpMain2();
@@ -6145,6 +6729,14 @@ DIAGNOSTICS & ONE-OFFS (no session needed)
6145
6729
  network <offline|online> Toggle connectivity for a tab
6146
6730
  har create | read [id] | delete <id> Manage standalone HAR recordings
6147
6731
 
6732
+ LIBRARY (vault libs \u2014 dev checkout only)
6733
+
6734
+ lib list List available libs
6735
+ lib search "<query>" [--limit N] Fuzzy-search functions across libs
6736
+ lib show <name> Lib + function summaries
6737
+ lib read <name> [fn\u2026] Full input/output schemas (+ .ts source path)
6738
+ exec "import {fn} from 'libs/<name>'; return await fn({\u2026})" Run a lib in the active tab
6739
+
6148
6740
  TARGETING (only when NOT in a session, or picking a parallel tab)
6149
6741
 
6150
6742
  --target <tabId> Exact id; a prefix of 8 chars is enough (preferred, parallel-safe)