@askthew/mcp-plugin 0.4.10 → 0.4.12

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.
@@ -1,606 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import { createRequire } from "node:module";
4
- import path from "node:path";
5
- import { jsonFallbackStorePath, localStorePath, readJsonFile, writePrivateJson } from "./paths.js";
6
- export class LocalStore {
7
- storePath;
8
- data;
9
- db = null;
10
- jsonMode = false;
11
- jsonPath;
12
- constructor(storePath, data) {
13
- this.storePath = storePath;
14
- this.data = data;
15
- this.jsonPath = jsonFallbackStorePath();
16
- }
17
- static open(input = {}) {
18
- const store = new LocalStore(input.path ?? localStorePath(), {
19
- meta: {},
20
- signals: [],
21
- decisions: [],
22
- telemetryOutbox: [],
23
- });
24
- store.openDatabase();
25
- return store;
26
- }
27
- get usingJsonFallback() {
28
- return this.jsonMode;
29
- }
30
- close() {
31
- this.db?.close();
32
- }
33
- migrate() {
34
- if (this.db) {
35
- this.db.exec(`
36
- create table if not exists meta (
37
- key text primary key,
38
- value text not null
39
- );
40
- create table if not exists sessions (
41
- session_id text primary key,
42
- started_at text not null,
43
- ended_at text,
44
- host_type text,
45
- repo_root text,
46
- metadata_json text not null default '{}'
47
- );
48
- create table if not exists signals (
49
- id integer primary key autoincrement,
50
- session_id text not null,
51
- sequence integer not null,
52
- kind text not null,
53
- summary text not null,
54
- evidence_json text not null default '[]',
55
- files_json text not null default '[]',
56
- commands_json text not null default '[]',
57
- metadata_json text not null default '{}',
58
- scope_key text,
59
- captured_at text not null,
60
- uploaded_at text,
61
- unique(session_id, sequence)
62
- );
63
- create table if not exists decisions (
64
- id text primary key,
65
- session_id text,
66
- headline text not null,
67
- why text,
68
- status text not null default 'proposed',
69
- alignment text,
70
- files_json text not null default '[]',
71
- source_signal_ids text not null default '[]',
72
- raw_content text not null,
73
- scope_key text,
74
- proposed_at text,
75
- committed_at text,
76
- shipped_at text,
77
- abandoned_at text,
78
- created_at text not null,
79
- updated_at text not null,
80
- uploaded_at text
81
- );
82
- create table if not exists telemetry_outbox (
83
- id integer primary key autoincrement,
84
- payload_json text not null,
85
- created_at text not null,
86
- attempts integer not null default 0,
87
- last_attempt_at text,
88
- delivered_at text
89
- );
90
- `);
91
- this.addColumnIfMissing("signals", "scope_key", "alter table signals add column scope_key text");
92
- this.addColumnIfMissing("decisions", "scope_key", "alter table decisions add column scope_key text");
93
- this.addColumnIfMissing("decisions", "proposed_at", "alter table decisions add column proposed_at text");
94
- this.addColumnIfMissing("decisions", "committed_at", "alter table decisions add column committed_at text");
95
- this.addColumnIfMissing("decisions", "shipped_at", "alter table decisions add column shipped_at text");
96
- this.addColumnIfMissing("decisions", "abandoned_at", "alter table decisions add column abandoned_at text");
97
- this.setMeta("schema_version", "2");
98
- return;
99
- }
100
- this.data.meta.schema_version = "2";
101
- this.persistJson();
102
- }
103
- getMeta(key) {
104
- if (this.db) {
105
- return String(this.db.prepare("select value from meta where key = ?").get(key)?.value ?? "");
106
- }
107
- return this.data.meta[key] ?? "";
108
- }
109
- setMeta(key, value) {
110
- if (this.db) {
111
- this.db
112
- .prepare("insert into meta (key, value) values (?, ?) on conflict(key) do update set value = excluded.value")
113
- .run(key, value);
114
- return;
115
- }
116
- this.data.meta[key] = value;
117
- this.persistJson();
118
- }
119
- insertSignal(input) {
120
- const now = new Date().toISOString();
121
- if (this.db) {
122
- const existing = this.db
123
- .prepare("select * from signals where session_id = ? and sequence = ?")
124
- .get(input.sessionId, input.sequence);
125
- if (existing) {
126
- return rowToSignal(existing);
127
- }
128
- const result = this.db
129
- .prepare(`insert into signals
130
- (session_id, sequence, kind, summary, evidence_json, files_json, commands_json, metadata_json, scope_key, captured_at)
131
- values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
132
- .run(input.sessionId, input.sequence, input.kind, input.summary, JSON.stringify(input.evidence ?? []), JSON.stringify(input.filesTouched ?? []), JSON.stringify(input.commandsRun ?? []), JSON.stringify(input.metadata ?? {}), input.scopeKey ?? null, now);
133
- const id = Number(result.lastInsertRowid ?? 0);
134
- return this.getSignal(id);
135
- }
136
- const existing = this.data.signals.find((signal) => signal.sessionId === input.sessionId && signal.sequence === input.sequence);
137
- if (existing) {
138
- return existing;
139
- }
140
- const signal = {
141
- id: this.nextJsonId(this.data.signals),
142
- sessionId: input.sessionId,
143
- sequence: input.sequence,
144
- kind: input.kind,
145
- summary: input.summary,
146
- evidence: input.evidence ?? [],
147
- filesTouched: input.filesTouched ?? [],
148
- commandsRun: input.commandsRun ?? [],
149
- metadata: input.metadata ?? {},
150
- scopeKey: input.scopeKey ?? null,
151
- capturedAt: now,
152
- };
153
- this.data.signals.push(signal);
154
- this.persistJson();
155
- return signal;
156
- }
157
- listSignals(input = {}) {
158
- const limit = input.limit ?? 300;
159
- if (this.db) {
160
- const clauses = [];
161
- const params = [];
162
- if (input.sessionId) {
163
- clauses.push("session_id = ?");
164
- params.push(input.sessionId);
165
- }
166
- if (input.scopeKey) {
167
- clauses.push("scope_key = ?");
168
- params.push(input.scopeKey);
169
- }
170
- if (input.since) {
171
- clauses.push("captured_at >= ?");
172
- params.push(input.since);
173
- }
174
- if (input.cursor) {
175
- const cursorTime = new Date(input.cursor).getTime();
176
- if (Number.isFinite(cursorTime)) {
177
- clauses.push("captured_at > ?");
178
- params.push(new Date(cursorTime).toISOString());
179
- }
180
- else if (/^\d+$/.test(input.cursor)) {
181
- clauses.push("id > ?");
182
- params.push(Number(input.cursor));
183
- }
184
- }
185
- const where = clauses.length > 0 ? `where ${clauses.join(" and ")}` : "";
186
- const rows = this.db
187
- .prepare(`select * from signals ${where} order by captured_at asc, id asc limit ?`)
188
- .all(...params, limit);
189
- return rows.map(rowToSignal);
190
- }
191
- return this.data.signals
192
- .filter((signal) => !input.sessionId || signal.sessionId === input.sessionId)
193
- .filter((signal) => !input.scopeKey || signal.scopeKey === input.scopeKey)
194
- .filter((signal) => !input.since || signal.capturedAt >= input.since)
195
- .filter((signal) => {
196
- if (!input.cursor)
197
- return true;
198
- const cursorTime = new Date(input.cursor).getTime();
199
- if (Number.isFinite(cursorTime))
200
- return signal.capturedAt > new Date(cursorTime).toISOString();
201
- if (/^\d+$/.test(input.cursor))
202
- return signal.id > Number(input.cursor);
203
- return true;
204
- })
205
- .sort((a, b) => a.capturedAt.localeCompare(b.capturedAt) || a.id - b.id)
206
- .slice(0, limit);
207
- }
208
- getSignal(id) {
209
- if (this.db) {
210
- const row = this.db.prepare("select * from signals where id = ?").get(id);
211
- return row ? rowToSignal(row) : null;
212
- }
213
- return this.data.signals.find((signal) => signal.id === id) ?? null;
214
- }
215
- mostRecentSessionId(input = {}) {
216
- if (this.db) {
217
- const row = input.scopeKey
218
- ? this.db.prepare("select session_id from signals where scope_key = ? order by captured_at desc, id desc limit 1").get(input.scopeKey)
219
- : this.db.prepare("select session_id from signals order by captured_at desc, id desc limit 1").get();
220
- return typeof row?.session_id === "string" ? row.session_id : null;
221
- }
222
- return this.data.signals
223
- .filter((signal) => !input.scopeKey || signal.scopeKey === input.scopeKey)
224
- .sort((left, right) => left.capturedAt.localeCompare(right.capturedAt) || left.id - right.id)
225
- .at(-1)?.sessionId ?? null;
226
- }
227
- createDecision(input) {
228
- const now = new Date().toISOString();
229
- const id = input.id ?? createLocalId();
230
- const raw = input.rawContent.trim();
231
- const decision = {
232
- id,
233
- sessionId: input.sessionId ?? null,
234
- headline: (input.headline ?? raw.split(/\n+/)[0] ?? raw).slice(0, 80),
235
- why: input.why ?? null,
236
- status: input.status ?? "proposed",
237
- alignment: input.alignment ?? null,
238
- files: input.files ?? [],
239
- sourceSignalIds: input.sourceSignalIds ?? [],
240
- rawContent: raw,
241
- createdAt: now,
242
- updatedAt: now,
243
- uploadedAt: null,
244
- scopeKey: input.scopeKey ?? null,
245
- proposedAt: (input.status ?? "proposed") === "proposed" ? now : null,
246
- committedAt: input.status === "committed" ? now : null,
247
- shippedAt: input.status === "shipped" ? now : null,
248
- abandonedAt: input.status === "abandoned" ? now : null,
249
- };
250
- if (this.db) {
251
- this.db
252
- .prepare(`insert into decisions
253
- (id, session_id, headline, why, status, alignment, files_json, source_signal_ids, raw_content, scope_key, proposed_at, committed_at, shipped_at, abandoned_at, created_at, updated_at, uploaded_at)
254
- values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
255
- .run(decision.id, decision.sessionId, decision.headline, decision.why, decision.status, decision.alignment, JSON.stringify(decision.files), JSON.stringify(decision.sourceSignalIds), decision.rawContent, decision.scopeKey, decision.proposedAt, decision.committedAt, decision.shippedAt, decision.abandonedAt, decision.createdAt, decision.updatedAt, decision.uploadedAt);
256
- return decision;
257
- }
258
- this.data.decisions.push(decision);
259
- this.persistJson();
260
- return decision;
261
- }
262
- updateDecision(id, patch) {
263
- const existing = this.getDecision(id);
264
- if (!existing) {
265
- return null;
266
- }
267
- const next = {
268
- ...existing,
269
- ...patch,
270
- updatedAt: new Date().toISOString(),
271
- };
272
- if (patch.status && patch.status !== existing.status) {
273
- if (patch.status === "proposed" && !next.proposedAt)
274
- next.proposedAt = next.updatedAt;
275
- if (patch.status === "committed" && !next.committedAt)
276
- next.committedAt = next.updatedAt;
277
- if (patch.status === "shipped" && !next.shippedAt)
278
- next.shippedAt = next.updatedAt;
279
- if (patch.status === "abandoned" && !next.abandonedAt)
280
- next.abandonedAt = next.updatedAt;
281
- }
282
- if (this.db) {
283
- this.db
284
- .prepare(`update decisions set headline = ?, why = ?, status = ?, alignment = ?, files_json = ?,
285
- source_signal_ids = ?, raw_content = ?, scope_key = ?, proposed_at = ?, committed_at = ?,
286
- shipped_at = ?, abandoned_at = ?, updated_at = ?, uploaded_at = ? where id = ?`)
287
- .run(next.headline, next.why, next.status, next.alignment, JSON.stringify(next.files), JSON.stringify(next.sourceSignalIds), next.rawContent, next.scopeKey, next.proposedAt, next.committedAt, next.shippedAt, next.abandonedAt, next.updatedAt, next.uploadedAt, id);
288
- return next;
289
- }
290
- this.data.decisions = this.data.decisions.map((decision) => (decision.id === id ? next : decision));
291
- this.persistJson();
292
- return next;
293
- }
294
- deleteDecision(id) {
295
- if (this.db) {
296
- return Number(this.db.prepare("delete from decisions where id = ?").run(id).changes ?? 0) > 0;
297
- }
298
- const before = this.data.decisions.length;
299
- this.data.decisions = this.data.decisions.filter((decision) => decision.id !== id);
300
- this.persistJson();
301
- return this.data.decisions.length !== before;
302
- }
303
- getDecision(id) {
304
- if (this.db) {
305
- const row = this.db.prepare("select * from decisions where id = ?").get(id);
306
- return row ? rowToDecision(row) : null;
307
- }
308
- return this.data.decisions.find((decision) => decision.id === id) ?? null;
309
- }
310
- listDecisions(input = {}) {
311
- const limit = input.limit ?? 50;
312
- if (this.db) {
313
- const clauses = [];
314
- const params = [];
315
- if (input.sessionId) {
316
- clauses.push("session_id = ?");
317
- params.push(input.sessionId);
318
- }
319
- if (input.scopeKey) {
320
- clauses.push("scope_key = ?");
321
- params.push(input.scopeKey);
322
- }
323
- if (input.status) {
324
- clauses.push("status = ?");
325
- params.push(input.status);
326
- }
327
- if (input.since) {
328
- clauses.push("created_at >= ?");
329
- params.push(input.since);
330
- }
331
- if (input.cursor) {
332
- const cursorTime = new Date(input.cursor).getTime();
333
- if (Number.isFinite(cursorTime)) {
334
- clauses.push("created_at < ?");
335
- params.push(new Date(cursorTime).toISOString());
336
- }
337
- else {
338
- clauses.push("id < ?");
339
- params.push(input.cursor);
340
- }
341
- }
342
- if (input.pendingUploadOnly) {
343
- clauses.push("uploaded_at is null");
344
- }
345
- const where = clauses.length > 0 ? `where ${clauses.join(" and ")}` : "";
346
- return this.db
347
- .prepare(`select * from decisions ${where} order by created_at desc limit ?`)
348
- .all(...params, limit)
349
- .map(rowToDecision);
350
- }
351
- return this.data.decisions
352
- .filter((decision) => !input.sessionId || decision.sessionId === input.sessionId)
353
- .filter((decision) => !input.scopeKey || decision.scopeKey === input.scopeKey)
354
- .filter((decision) => !input.status || decision.status === input.status)
355
- .filter((decision) => !input.since || decision.createdAt >= input.since)
356
- .filter((decision) => {
357
- if (!input.cursor)
358
- return true;
359
- const cursorTime = new Date(input.cursor).getTime();
360
- if (Number.isFinite(cursorTime))
361
- return decision.createdAt < new Date(cursorTime).toISOString();
362
- return decision.id < input.cursor;
363
- })
364
- .filter((decision) => !input.pendingUploadOnly || !decision.uploadedAt)
365
- .sort((a, b) => b.createdAt.localeCompare(a.createdAt))
366
- .slice(0, limit);
367
- }
368
- listSessionIds(input = {}) {
369
- const limit = input.limit ?? 100000;
370
- if (this.db) {
371
- const rows = this.db
372
- .prepare(`select session_id, max(captured_at) as last_seen_at
373
- from signals
374
- ${input.scopeKey ? "where scope_key = ?" : ""}
375
- group by session_id
376
- order by last_seen_at desc
377
- limit ?`)
378
- .all(...(input.scopeKey ? [input.scopeKey, limit] : [limit]));
379
- return rows.map((row) => String(row.session_id));
380
- }
381
- const lastSeen = new Map();
382
- for (const signal of this.data.signals) {
383
- if (input.scopeKey && signal.scopeKey !== input.scopeKey) {
384
- continue;
385
- }
386
- const current = lastSeen.get(signal.sessionId);
387
- if (!current || signal.capturedAt > current) {
388
- lastSeen.set(signal.sessionId, signal.capturedAt);
389
- }
390
- }
391
- return Array.from(lastSeen.entries())
392
- .sort((left, right) => right[1].localeCompare(left[1]))
393
- .slice(0, limit)
394
- .map(([sessionId]) => sessionId);
395
- }
396
- listSignalsByIds(ids) {
397
- const uniqueIds = Array.from(new Set(ids.filter((id) => Number.isFinite(id))));
398
- if (uniqueIds.length === 0) {
399
- return [];
400
- }
401
- if (this.db) {
402
- const placeholders = uniqueIds.map(() => "?").join(", ");
403
- return this.db
404
- .prepare(`select * from signals where id in (${placeholders}) order by captured_at asc, id asc`)
405
- .all(...uniqueIds)
406
- .map(rowToSignal);
407
- }
408
- const idSet = new Set(uniqueIds);
409
- return this.data.signals
410
- .filter((signal) => idSet.has(signal.id))
411
- .sort((a, b) => a.capturedAt.localeCompare(b.capturedAt) || a.id - b.id);
412
- }
413
- getDecisionForSignal(signalId) {
414
- if (this.db) {
415
- const rows = this.db.prepare("select * from decisions order by created_at desc").all();
416
- for (const row of rows) {
417
- const decision = rowToDecision(row);
418
- if (decision.sourceSignalIds.includes(signalId)) {
419
- return decision;
420
- }
421
- }
422
- return null;
423
- }
424
- return (this.data.decisions
425
- .slice()
426
- .sort((left, right) => right.createdAt.localeCompare(left.createdAt))
427
- .find((decision) => decision.sourceSignalIds.includes(signalId)) ?? null);
428
- }
429
- enqueueTelemetry(payload) {
430
- const now = new Date().toISOString();
431
- if (this.db) {
432
- const result = this.db
433
- .prepare("insert into telemetry_outbox (payload_json, created_at) values (?, ?)")
434
- .run(JSON.stringify(payload), now);
435
- return Number(result.lastInsertRowid ?? 0);
436
- }
437
- const id = this.nextJsonId(this.data.telemetryOutbox);
438
- this.data.telemetryOutbox.push({
439
- id,
440
- payload,
441
- createdAt: now,
442
- attempts: 0,
443
- lastAttemptAt: null,
444
- deliveredAt: null,
445
- });
446
- this.persistJson();
447
- return id;
448
- }
449
- listTelemetryOutbox(input = {}) {
450
- const limit = input.limit ?? 20;
451
- if (this.db) {
452
- const where = input.undeliveredOnly ? "where delivered_at is null" : "";
453
- return this.db
454
- .prepare(`select * from telemetry_outbox ${where} order by id asc limit ?`)
455
- .all(limit)
456
- .map(rowToTelemetry);
457
- }
458
- return this.data.telemetryOutbox
459
- .filter((row) => !input.undeliveredOnly || !row.deliveredAt)
460
- .sort((a, b) => a.id - b.id)
461
- .slice(0, limit);
462
- }
463
- markTelemetryAttempt(id, delivered) {
464
- const now = new Date().toISOString();
465
- if (this.db) {
466
- this.db
467
- .prepare(`update telemetry_outbox
468
- set attempts = attempts + 1, last_attempt_at = ?, delivered_at = case when ? then ? else delivered_at end
469
- where id = ?`)
470
- .run(now, delivered ? 1 : 0, now, id);
471
- return;
472
- }
473
- this.data.telemetryOutbox = this.data.telemetryOutbox.map((row) => row.id === id
474
- ? {
475
- ...row,
476
- attempts: row.attempts + 1,
477
- lastAttemptAt: now,
478
- deliveredAt: delivered ? now : row.deliveredAt,
479
- }
480
- : row);
481
- this.persistJson();
482
- }
483
- stats() {
484
- const signals = this.listSignals({ limit: 100000 });
485
- const decisions = this.listDecisions({ limit: 100000 });
486
- return {
487
- signals: signals.length,
488
- decisions: decisions.length,
489
- decisionsByStatus: decisions.reduce((accumulator, decision) => {
490
- accumulator[decision.status] = (accumulator[decision.status] ?? 0) + 1;
491
- return accumulator;
492
- }, {}),
493
- lastSessionId: signals.at(-1)?.sessionId ?? null,
494
- jsonFallback: this.usingJsonFallback,
495
- };
496
- }
497
- openDatabase() {
498
- const loaded = tryRequireBetterSqlite3();
499
- if (loaded) {
500
- try {
501
- fs.mkdirSync(path.dirname(this.storePath), { recursive: true, mode: 0o700 });
502
- this.db = new loaded(this.storePath);
503
- this.migrate();
504
- return;
505
- }
506
- catch {
507
- try {
508
- this.db?.close();
509
- }
510
- catch {
511
- // Ignore close failures while falling back to the JSON store.
512
- }
513
- this.db = null;
514
- }
515
- }
516
- this.jsonMode = true;
517
- this.jsonPath = jsonFallbackStorePath();
518
- this.data = readJsonFile(this.jsonPath) ?? this.data;
519
- this.migrate();
520
- }
521
- persistJson() {
522
- writePrivateJson(this.jsonPath, this.data);
523
- }
524
- nextJsonId(rows) {
525
- return rows.reduce((max, row) => Math.max(max, row.id), 0) + 1;
526
- }
527
- addColumnIfMissing(table, column, statement) {
528
- if (!this.db)
529
- return;
530
- const rows = this.db.prepare(`pragma table_info(${table})`).all();
531
- if (rows.some((row) => row.name === column)) {
532
- return;
533
- }
534
- this.db.exec(statement);
535
- }
536
- }
537
- function tryRequireBetterSqlite3() {
538
- try {
539
- // createRequire keeps the CLI startup synchronous while still letting unsupported platforms fall back cleanly.
540
- const req = createRequire(import.meta.url);
541
- return req("better-sqlite3");
542
- }
543
- catch {
544
- return null;
545
- }
546
- }
547
- function parseJson(value, fallback) {
548
- if (typeof value !== "string") {
549
- return fallback;
550
- }
551
- try {
552
- return JSON.parse(value);
553
- }
554
- catch {
555
- return fallback;
556
- }
557
- }
558
- function rowToSignal(row) {
559
- return {
560
- id: Number(row.id),
561
- sessionId: String(row.session_id),
562
- sequence: Number(row.sequence),
563
- kind: row.kind,
564
- summary: String(row.summary),
565
- evidence: parseJson(row.evidence_json, []),
566
- filesTouched: parseJson(row.files_json, []),
567
- commandsRun: parseJson(row.commands_json, []),
568
- metadata: parseJson(row.metadata_json, {}),
569
- scopeKey: typeof row.scope_key === "string" ? row.scope_key : null,
570
- capturedAt: String(row.captured_at),
571
- };
572
- }
573
- function rowToDecision(row) {
574
- return {
575
- id: String(row.id),
576
- sessionId: typeof row.session_id === "string" ? row.session_id : null,
577
- headline: String(row.headline),
578
- why: typeof row.why === "string" ? row.why : null,
579
- status: row.status,
580
- alignment: typeof row.alignment === "string" ? row.alignment : null,
581
- files: parseJson(row.files_json, []),
582
- sourceSignalIds: parseJson(row.source_signal_ids, []),
583
- rawContent: String(row.raw_content),
584
- createdAt: String(row.created_at),
585
- updatedAt: String(row.updated_at),
586
- uploadedAt: typeof row.uploaded_at === "string" ? row.uploaded_at : null,
587
- scopeKey: typeof row.scope_key === "string" ? row.scope_key : null,
588
- proposedAt: typeof row.proposed_at === "string" ? row.proposed_at : null,
589
- committedAt: typeof row.committed_at === "string" ? row.committed_at : null,
590
- shippedAt: typeof row.shipped_at === "string" ? row.shipped_at : null,
591
- abandonedAt: typeof row.abandoned_at === "string" ? row.abandoned_at : null,
592
- };
593
- }
594
- function rowToTelemetry(row) {
595
- return {
596
- id: Number(row.id),
597
- payload: parseJson(row.payload_json, {}),
598
- createdAt: String(row.created_at),
599
- attempts: Number(row.attempts),
600
- lastAttemptAt: typeof row.last_attempt_at === "string" ? row.last_attempt_at : null,
601
- deliveredAt: typeof row.delivered_at === "string" ? row.delivered_at : null,
602
- };
603
- }
604
- function createLocalId() {
605
- return `d_${crypto.randomBytes(8).toString("hex")}`;
606
- }
@@ -1,8 +0,0 @@
1
- export declare function waitForLoopbackToken(input?: {
2
- timeoutMs?: number;
3
- path?: string;
4
- tokenParam?: string;
5
- }): Promise<{
6
- token: string;
7
- port: number;
8
- }>;
@@ -1,30 +0,0 @@
1
- import http from "node:http";
2
- export async function waitForLoopbackToken(input = {}) {
3
- const timeoutMs = input.timeoutMs ?? 120_000;
4
- const callbackPath = input.path ?? "/auth-callback";
5
- const tokenParam = input.tokenParam ?? "token";
6
- return new Promise((resolve, reject) => {
7
- const server = http.createServer((request, response) => {
8
- const url = new URL(request.url ?? "/", "http://127.0.0.1");
9
- if (url.pathname !== callbackPath) {
10
- response.writeHead(404).end("Not found");
11
- return;
12
- }
13
- const token = url.searchParams.get(tokenParam)?.trim();
14
- if (!token) {
15
- response.writeHead(400).end("Missing token");
16
- return;
17
- }
18
- response.writeHead(200, { "Content-Type": "text/plain" }).end("Ask The W upgrade authorized. You can return to your terminal.");
19
- server.close();
20
- resolve({ token, port: Number(server.address()?.port ?? 0) });
21
- });
22
- const timer = setTimeout(() => {
23
- server.close();
24
- reject(new Error("Timed out waiting for browser authorization."));
25
- }, timeoutMs);
26
- server.once("close", () => clearTimeout(timer));
27
- server.once("error", reject);
28
- server.listen(0, "127.0.0.1");
29
- });
30
- }
@@ -1,25 +0,0 @@
1
- import { type CliCredentials } from "./free-tier-policy.js";
2
- import type { LocalStore } from "./local-store.js";
3
- export declare function buildTelemetryPayload(input: {
4
- store: LocalStore;
5
- credentials: CliCredentials;
6
- sessionId?: string;
7
- toolUsage?: Record<string, number>;
8
- now?: Date;
9
- cwd?: string;
10
- }): unknown;
11
- export declare function flushTelemetryOutbox(input: {
12
- store: LocalStore;
13
- credentials: CliCredentials;
14
- apiUrl?: string;
15
- fetchImpl?: typeof fetch;
16
- env?: NodeJS.ProcessEnv;
17
- }): Promise<{
18
- ok: boolean;
19
- skipped: boolean;
20
- sent: number;
21
- } | {
22
- ok: boolean;
23
- sent: number;
24
- skipped?: undefined;
25
- }>;