getpatter 0.5.2 → 0.5.3

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.
@@ -0,0 +1,25 @@
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+ var __glob = (map) => (path) => {
9
+ var fn = map[path];
10
+ if (fn) return fn();
11
+ throw new Error("Module not found in bundle: " + path);
12
+ };
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ };
16
+ var __commonJS = (cb, mod) => function __require2() {
17
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
+ };
19
+
20
+ export {
21
+ __require,
22
+ __glob,
23
+ __esm,
24
+ __commonJS
25
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getLogger
3
- } from "./chunk-FMNRCP5X.mjs";
3
+ } from "./chunk-VJVDG4V5.mjs";
4
4
 
5
5
  // src/tunnel.ts
6
6
  var log = getLogger();
@@ -17,9 +17,14 @@ async function startTunnel(port, timeoutMs = 3e4) {
17
17
  const TunnelClass = tunnelMod.Tunnel;
18
18
  const hasQuick = TunnelClass && typeof TunnelClass.quick === "function";
19
19
  let instance;
20
- if (hasQuick) {
20
+ if (hasQuick && TunnelClass?.quick) {
21
21
  instance = TunnelClass.quick(`http://localhost:${port}`);
22
22
  } else {
23
+ if (!tunnelMod.tunnel) {
24
+ throw new Error(
25
+ 'Built-in tunnel: installed "cloudflared" package exposes neither `Tunnel.quick` nor `tunnel({ "--url" })`. Upgrade with `npm install cloudflared@latest`.'
26
+ );
27
+ }
23
28
  const result = tunnelMod.tunnel({ "--url": `http://localhost:${port}` });
24
29
  if (result.url && typeof result.url.then === "function") {
25
30
  const tunnelUrl2 = await Promise.race([
@@ -1,6 +1,6 @@
1
1
  // src/logger.ts
2
2
  var defaultLogger = {
3
- info: (msg, ...args) => console.log(`[PATTER] ${msg}`, ...args),
3
+ info: (msg, ...args) => console.info(`[PATTER] ${msg}`, ...args),
4
4
  warn: (msg, ...args) => console.warn(`[PATTER] WARNING: ${msg}`, ...args),
5
5
  error: (msg, ...args) => console.error(`[PATTER] ERROR: ${msg}`, ...args),
6
6
  debug: () => {
package/dist/cli.js CHANGED
@@ -29,6 +29,23 @@ var import_express = __toESM(require("express"));
29
29
 
30
30
  // src/dashboard/store.ts
31
31
  var import_events = require("events");
32
+ var fs = __toESM(require("fs"));
33
+ var path = __toESM(require("path"));
34
+
35
+ // src/logger.ts
36
+ var defaultLogger = {
37
+ info: (msg, ...args) => console.info(`[PATTER] ${msg}`, ...args),
38
+ warn: (msg, ...args) => console.warn(`[PATTER] WARNING: ${msg}`, ...args),
39
+ error: (msg, ...args) => console.error(`[PATTER] ERROR: ${msg}`, ...args),
40
+ debug: () => {
41
+ }
42
+ };
43
+ var currentLogger = defaultLogger;
44
+ function getLogger() {
45
+ return currentLogger;
46
+ }
47
+
48
+ // src/dashboard/store.ts
32
49
  var MetricsStore = class extends import_events.EventEmitter {
33
50
  maxCalls;
34
51
  calls = [];
@@ -191,6 +208,10 @@ var MetricsStore = class extends import_events.EventEmitter {
191
208
  }
192
209
  return null;
193
210
  }
211
+ /** Look up an active call by id (returns undefined if not active or unknown). */
212
+ getActive(callId) {
213
+ return this.activeCalls.get(callId);
214
+ }
194
215
  getActiveCalls() {
195
216
  return Array.from(this.activeCalls.values());
196
217
  }
@@ -256,7 +277,102 @@ var MetricsStore = class extends import_events.EventEmitter {
256
277
  get callCount() {
257
278
  return this.calls.length;
258
279
  }
280
+ /**
281
+ * Rebuild the in-memory call list from `metadata.json` files written by
282
+ * `CallLogger` under `<logRoot>/calls/YYYY/MM/DD/<call_id>/`. Idempotent:
283
+ * call_ids already in the store are skipped. Errors per file are logged
284
+ * and swallowed so a single corrupt entry doesn't block hydration.
285
+ *
286
+ * Returns the number of calls newly added to the store.
287
+ *
288
+ * Safe to call before any traffic; intended to run once at server startup.
289
+ */
290
+ hydrate(logRoot) {
291
+ if (!logRoot) return 0;
292
+ const callsRoot = path.join(logRoot, "calls");
293
+ if (!fs.existsSync(callsRoot)) return 0;
294
+ const collected = [];
295
+ const seen = new Set(this.calls.map((c) => c.call_id));
296
+ const walk = (dir, depth) => {
297
+ let entries;
298
+ try {
299
+ entries = fs.readdirSync(dir, { withFileTypes: true });
300
+ } catch {
301
+ return;
302
+ }
303
+ for (const entry of entries) {
304
+ const childPath = path.join(dir, entry.name);
305
+ if (depth < 3) {
306
+ if (entry.isDirectory() && /^\d+$/.test(entry.name)) {
307
+ walk(childPath, depth + 1);
308
+ }
309
+ continue;
310
+ }
311
+ if (!entry.isDirectory()) continue;
312
+ const metadataPath = path.join(childPath, "metadata.json");
313
+ if (!fs.existsSync(metadataPath)) continue;
314
+ try {
315
+ const raw = fs.readFileSync(metadataPath, "utf8");
316
+ const meta = JSON.parse(raw);
317
+ const callId = meta.call_id || entry.name;
318
+ if (!callId || seen.has(callId)) continue;
319
+ const record = metadataToCallRecord(callId, meta);
320
+ if (record === null) {
321
+ getLogger().debug(
322
+ `MetricsStore.hydrate: skipping ${metadataPath}: unparseable started_at`
323
+ );
324
+ continue;
325
+ }
326
+ collected.push(record);
327
+ seen.add(callId);
328
+ } catch (err) {
329
+ getLogger().debug(
330
+ `MetricsStore.hydrate: skipping ${metadataPath}: ${String(err)}`
331
+ );
332
+ }
333
+ }
334
+ };
335
+ walk(callsRoot, 0);
336
+ collected.sort((a, b) => (a.started_at || 0) - (b.started_at || 0));
337
+ for (const rec of collected) {
338
+ if (this.calls.some((c) => c.call_id === rec.call_id)) continue;
339
+ this.calls.push(rec);
340
+ if (this.calls.length > this.maxCalls) {
341
+ this.calls = this.calls.slice(-this.maxCalls);
342
+ }
343
+ }
344
+ return collected.length;
345
+ }
259
346
  };
347
+ function metadataToCallRecord(callId, meta) {
348
+ const startedAt = parseTimestamp(meta.started_at);
349
+ if (startedAt === null) return null;
350
+ const endedAt = parseTimestamp(meta.ended_at);
351
+ const status = meta.status || "completed";
352
+ const metrics = meta.metrics && typeof meta.metrics === "object" ? meta.metrics : null;
353
+ const transcript = Array.isArray(meta.transcript) ? meta.transcript : [];
354
+ return {
355
+ call_id: callId,
356
+ caller: meta.caller || "",
357
+ callee: meta.callee || "",
358
+ direction: meta.direction || "inbound",
359
+ started_at: startedAt,
360
+ ended_at: endedAt ?? void 0,
361
+ status,
362
+ metrics,
363
+ transcript
364
+ };
365
+ }
366
+ function parseTimestamp(raw) {
367
+ if (typeof raw === "number") {
368
+ return Number.isFinite(raw) ? raw : null;
369
+ }
370
+ if (typeof raw === "string") {
371
+ const ms = Date.parse(raw);
372
+ return Number.isFinite(ms) ? ms / 1e3 : null;
373
+ }
374
+ return null;
375
+ }
260
376
 
261
377
  // src/dashboard/auth.ts
262
378
  var import_node_crypto = __toESM(require("crypto"));
@@ -1130,19 +1246,6 @@ function mountApi(app, store, token = "") {
1130
1246
  });
1131
1247
  }
1132
1248
 
1133
- // src/logger.ts
1134
- var defaultLogger = {
1135
- info: (msg, ...args) => console.log(`[PATTER] ${msg}`, ...args),
1136
- warn: (msg, ...args) => console.warn(`[PATTER] WARNING: ${msg}`, ...args),
1137
- error: (msg, ...args) => console.error(`[PATTER] ERROR: ${msg}`, ...args),
1138
- debug: () => {
1139
- }
1140
- };
1141
- var currentLogger = defaultLogger;
1142
- function getLogger() {
1143
- return currentLogger;
1144
- }
1145
-
1146
1249
  // src/banner.ts
1147
1250
  var BANNER = `
1148
1251
  \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
@@ -1174,10 +1277,20 @@ function parseArgs(argv) {
1174
1277
  }
1175
1278
  return { port };
1176
1279
  }
1280
+ function printEvalStub() {
1281
+ console.log(
1282
+ "Evaluations are not yet available in the TypeScript SDK.\nUse the Python SDK instead:\n\n pip install getpatter\n patter eval --help\n\nSee https://github.com/PatterAI/Patter for docs."
1283
+ );
1284
+ }
1177
1285
  async function main() {
1178
1286
  const command = process.argv[2];
1287
+ if (command === "eval") {
1288
+ printEvalStub();
1289
+ process.exit(0);
1290
+ }
1179
1291
  if (command !== "dashboard") {
1180
1292
  console.log("Usage: getpatter dashboard [--port 8000]");
1293
+ console.log(" getpatter eval (stub \u2014 use Python SDK for evals)");
1181
1294
  process.exit(command ? 1 : 0);
1182
1295
  }
1183
1296
  const { port } = parseArgs(process.argv);