@oka-core/reason 0.2.14 → 0.2.16

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 (163) hide show
  1. package/dist/abort-controller.d.ts +19 -0
  2. package/dist/abort-controller.d.ts.map +1 -0
  3. package/dist/abort-controller.js +53 -0
  4. package/dist/activity-tracker.d.ts +48 -0
  5. package/dist/activity-tracker.d.ts.map +1 -0
  6. package/dist/activity-tracker.js +80 -0
  7. package/dist/analytics.d.ts +49 -0
  8. package/dist/analytics.d.ts.map +1 -0
  9. package/dist/analytics.js +88 -0
  10. package/dist/array.d.ts +12 -0
  11. package/dist/array.d.ts.map +1 -0
  12. package/dist/array.js +20 -0
  13. package/dist/async-context.d.ts +20 -0
  14. package/dist/async-context.d.ts.map +1 -0
  15. package/dist/async-context.js +25 -0
  16. package/dist/auth.d.ts +15 -0
  17. package/dist/auth.d.ts.map +1 -1
  18. package/dist/auth.js +77 -0
  19. package/dist/binary-check.d.ts +16 -0
  20. package/dist/binary-check.d.ts.map +1 -0
  21. package/dist/binary-check.js +43 -0
  22. package/dist/buffered-writer.d.ts +30 -0
  23. package/dist/buffered-writer.d.ts.map +1 -0
  24. package/dist/buffered-writer.js +87 -0
  25. package/dist/circular-buffer.d.ts +28 -0
  26. package/dist/circular-buffer.d.ts.map +1 -0
  27. package/dist/circular-buffer.js +61 -0
  28. package/dist/cleanup-registry.d.ts +23 -0
  29. package/dist/cleanup-registry.d.ts.map +1 -0
  30. package/dist/cleanup-registry.js +34 -0
  31. package/dist/client.d.ts +6 -5
  32. package/dist/client.d.ts.map +1 -1
  33. package/dist/client.js +51 -64
  34. package/dist/combined-abort-signal.d.ts +25 -0
  35. package/dist/combined-abort-signal.d.ts.map +1 -0
  36. package/dist/combined-abort-signal.js +47 -0
  37. package/dist/cron-lock.d.ts +29 -0
  38. package/dist/cron-lock.d.ts.map +1 -0
  39. package/dist/cron-lock.js +127 -0
  40. package/dist/cron-scheduler.d.ts +41 -0
  41. package/dist/cron-scheduler.d.ts.map +1 -0
  42. package/dist/cron-scheduler.js +189 -0
  43. package/dist/cron-tasks.d.ts +86 -0
  44. package/dist/cron-tasks.d.ts.map +1 -0
  45. package/dist/cron-tasks.js +205 -0
  46. package/dist/cron.d.ts +35 -0
  47. package/dist/cron.d.ts.map +1 -0
  48. package/dist/cron.js +215 -0
  49. package/dist/env.d.ts +26 -0
  50. package/dist/env.d.ts.map +1 -0
  51. package/dist/env.js +50 -0
  52. package/dist/errors.d.ts +99 -0
  53. package/dist/errors.d.ts.map +1 -0
  54. package/dist/errors.js +214 -0
  55. package/dist/format.d.ts +21 -0
  56. package/dist/format.d.ts.map +1 -0
  57. package/dist/format.js +48 -0
  58. package/dist/fps-tracker.d.ts +22 -0
  59. package/dist/fps-tracker.d.ts.map +1 -0
  60. package/dist/fps-tracker.js +44 -0
  61. package/dist/graceful-shutdown.d.ts +35 -0
  62. package/dist/graceful-shutdown.d.ts.map +1 -0
  63. package/dist/graceful-shutdown.js +89 -0
  64. package/dist/hash.d.ts +21 -0
  65. package/dist/hash.d.ts.map +1 -0
  66. package/dist/hash.js +31 -0
  67. package/dist/heap-diagnostics.d.ts +68 -0
  68. package/dist/heap-diagnostics.d.ts.map +1 -0
  69. package/dist/heap-diagnostics.js +110 -0
  70. package/dist/idle-timeout.d.ts +21 -0
  71. package/dist/idle-timeout.d.ts.map +1 -0
  72. package/dist/idle-timeout.js +42 -0
  73. package/dist/index.d.ts +2 -1
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +5 -0
  76. package/dist/intl.d.ts +18 -0
  77. package/dist/intl.d.ts.map +1 -0
  78. package/dist/intl.js +75 -0
  79. package/dist/jsonl.d.ts +16 -0
  80. package/dist/jsonl.d.ts.map +1 -0
  81. package/dist/jsonl.js +60 -0
  82. package/dist/lazy-schema.d.ts +6 -0
  83. package/dist/lazy-schema.d.ts.map +1 -0
  84. package/dist/lazy-schema.js +8 -0
  85. package/dist/memo.d.ts +64 -0
  86. package/dist/memo.d.ts.map +1 -0
  87. package/dist/memo.js +162 -0
  88. package/dist/pkce.d.ts +13 -0
  89. package/dist/pkce.d.ts.map +1 -0
  90. package/dist/pkce.js +28 -0
  91. package/dist/priority-queue.d.ts +36 -0
  92. package/dist/priority-queue.d.ts.map +1 -0
  93. package/dist/priority-queue.js +97 -0
  94. package/dist/process-utils.d.ts +20 -0
  95. package/dist/process-utils.d.ts.map +1 -0
  96. package/dist/process-utils.js +54 -0
  97. package/dist/query-guard.d.ts +34 -0
  98. package/dist/query-guard.d.ts.map +1 -0
  99. package/dist/query-guard.js +74 -0
  100. package/dist/retry.d.ts +60 -0
  101. package/dist/retry.d.ts.map +1 -0
  102. package/dist/retry.js +89 -0
  103. package/dist/schemas.d.ts +6 -6
  104. package/dist/secrets.d.ts +44 -0
  105. package/dist/secrets.d.ts.map +1 -0
  106. package/dist/secrets.js +115 -0
  107. package/dist/semantic-types.d.ts +39 -0
  108. package/dist/semantic-types.d.ts.map +1 -0
  109. package/dist/semantic-types.js +49 -0
  110. package/dist/sequential.d.ts +21 -0
  111. package/dist/sequential.d.ts.map +1 -0
  112. package/dist/sequential.js +49 -0
  113. package/dist/signal.d.ts +29 -0
  114. package/dist/signal.d.ts.map +1 -0
  115. package/dist/signal.js +39 -0
  116. package/dist/sleep.d.ts +21 -0
  117. package/dist/sleep.d.ts.map +1 -0
  118. package/dist/sleep.js +58 -0
  119. package/dist/slow-ops.d.ts +41 -0
  120. package/dist/slow-ops.d.ts.map +1 -0
  121. package/dist/slow-ops.js +133 -0
  122. package/dist/store.d.ts +20 -0
  123. package/dist/store.d.ts.map +1 -0
  124. package/dist/store.js +34 -0
  125. package/dist/stream.d.ts +29 -0
  126. package/dist/stream.d.ts.map +1 -0
  127. package/dist/stream.js +92 -0
  128. package/dist/string-utils.d.ts +46 -0
  129. package/dist/string-utils.d.ts.map +1 -0
  130. package/dist/string-utils.js +69 -0
  131. package/dist/strip-bom.d.ts +8 -0
  132. package/dist/strip-bom.d.ts.map +1 -0
  133. package/dist/strip-bom.js +10 -0
  134. package/dist/subprocess-env.d.ts +25 -0
  135. package/dist/subprocess-env.d.ts.map +1 -0
  136. package/dist/subprocess-env.js +55 -0
  137. package/dist/temp-file.d.ts +18 -0
  138. package/dist/temp-file.d.ts.map +1 -0
  139. package/dist/temp-file.js +26 -0
  140. package/dist/tool-contract.d.ts +85 -0
  141. package/dist/tool-contract.d.ts.map +1 -0
  142. package/dist/tool-contract.js +101 -0
  143. package/dist/tools/auth.d.ts.map +1 -1
  144. package/dist/tools/auth.js +8 -7
  145. package/dist/tools/read.d.ts +2 -10
  146. package/dist/tools/read.d.ts.map +1 -1
  147. package/dist/tools/read.js +662 -537
  148. package/dist/tools/write.d.ts +3 -2
  149. package/dist/tools/write.d.ts.map +1 -1
  150. package/dist/tools/write.js +329 -177
  151. package/dist/uuid.d.ts +20 -0
  152. package/dist/uuid.d.ts.map +1 -0
  153. package/dist/uuid.js +28 -0
  154. package/dist/validation.d.ts +64 -0
  155. package/dist/validation.d.ts.map +1 -0
  156. package/dist/validation.js +236 -0
  157. package/dist/with-resolvers.d.ts +12 -0
  158. package/dist/with-resolvers.d.ts.map +1 -0
  159. package/dist/with-resolvers.js +14 -0
  160. package/dist/xml-escape.d.ts +12 -0
  161. package/dist/xml-escape.d.ts.map +1 -0
  162. package/dist/xml-escape.js +15 -0
  163. package/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ /**
2
+ * AbortController factory with listener-limit management and
3
+ * memory-safe parent→child propagation via WeakRef.
4
+ *
5
+ * Inspired by Claude Code's `src/utils/abortController.ts`.
6
+ */
7
+ /**
8
+ * Create an AbortController with raised listener limit to prevent
9
+ * MaxListenersExceededWarning when many consumers share one signal.
10
+ */
11
+ export declare function createAbortController(maxListeners?: number): AbortController;
12
+ /**
13
+ * Create a child AbortController that aborts when its parent aborts.
14
+ * Aborting the child does NOT affect the parent.
15
+ *
16
+ * Memory-safe: uses WeakRef so the parent doesn't retain abandoned children.
17
+ */
18
+ export declare function createChildAbortController(parent: AbortController, maxListeners?: number): AbortController;
19
+ //# sourceMappingURL=abort-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort-controller.d.ts","sourceRoot":"","sources":["../src/abort-controller.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,GAAE,MAA8B,GAC3C,eAAe,CAQjB;AAuBD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,eAAe,EACvB,YAAY,CAAC,EAAE,MAAM,GACpB,eAAe,CAqBjB"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * AbortController factory with listener-limit management and
3
+ * memory-safe parent→child propagation via WeakRef.
4
+ *
5
+ * Inspired by Claude Code's `src/utils/abortController.ts`.
6
+ */
7
+ import { setMaxListeners } from "events";
8
+ const DEFAULT_MAX_LISTENERS = 50;
9
+ /**
10
+ * Create an AbortController with raised listener limit to prevent
11
+ * MaxListenersExceededWarning when many consumers share one signal.
12
+ */
13
+ export function createAbortController(maxListeners = DEFAULT_MAX_LISTENERS) {
14
+ const controller = new AbortController();
15
+ try {
16
+ setMaxListeners(maxListeners, controller.signal);
17
+ }
18
+ catch {
19
+ /* runtime doesn't support setMaxListeners on EventTarget */
20
+ }
21
+ return controller;
22
+ }
23
+ // ── WeakRef-based parent→child propagation ────────────────────────
24
+ function propagateAbort(weakChild) {
25
+ const parent = this.deref();
26
+ weakChild.deref()?.abort(parent?.signal.reason);
27
+ }
28
+ function removeAbortHandler(weakHandler) {
29
+ const parent = this.deref();
30
+ const handler = weakHandler.deref();
31
+ if (parent && handler) {
32
+ parent.signal.removeEventListener("abort", handler);
33
+ }
34
+ }
35
+ /**
36
+ * Create a child AbortController that aborts when its parent aborts.
37
+ * Aborting the child does NOT affect the parent.
38
+ *
39
+ * Memory-safe: uses WeakRef so the parent doesn't retain abandoned children.
40
+ */
41
+ export function createChildAbortController(parent, maxListeners) {
42
+ const child = createAbortController(maxListeners);
43
+ if (parent.signal.aborted) {
44
+ child.abort(parent.signal.reason);
45
+ return child;
46
+ }
47
+ const weakChild = new WeakRef(child);
48
+ const weakParent = new WeakRef(parent);
49
+ const handler = propagateAbort.bind(weakParent, weakChild);
50
+ parent.signal.addEventListener("abort", handler, { once: true });
51
+ child.signal.addEventListener("abort", removeAbortHandler.bind(weakParent, new WeakRef(handler)), { once: true });
52
+ return child;
53
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Activity tracker for user vs CLI operation time tracking.
3
+ *
4
+ * Tracks overlapping operations with automatic deduplication.
5
+ * Injectable time source and callback for clean architecture.
6
+ *
7
+ * Extracted from Claude Code's ActivityManager — stripped singleton,
8
+ * bootstrap state, and metric counter coupling.
9
+ */
10
+ export type ActivityType = "user" | "cli";
11
+ export type ActivityRecord = {
12
+ type: ActivityType;
13
+ durationSeconds: number;
14
+ };
15
+ export type ActivityTrackerOptions = {
16
+ /** Time source (default: Date.now). */
17
+ getNow?: () => number;
18
+ /** Called when an activity period ends with its type and duration. */
19
+ onRecord?: (record: ActivityRecord) => void;
20
+ /** User inactivity timeout in ms (default: 5000). */
21
+ userTimeoutMs?: number;
22
+ };
23
+ export type ActivityStates = {
24
+ isUserActive: boolean;
25
+ isCliActive: boolean;
26
+ activeOperationCount: number;
27
+ };
28
+ export declare class ActivityTracker {
29
+ private activeOperations;
30
+ private lastUserActivityTime;
31
+ private lastCliRecordedTime;
32
+ private isCliActive;
33
+ private readonly getNow;
34
+ private readonly onRecord;
35
+ private readonly userTimeoutMs;
36
+ constructor(options?: ActivityTrackerOptions);
37
+ /** Record a user interaction (typing, clicking, etc.). */
38
+ recordUserActivity(): void;
39
+ /** Start tracking a CLI operation. Duplicate IDs are force-ended first. */
40
+ startActivity(operationId: string): void;
41
+ /** End tracking a CLI operation. Records CLI time when last op ends. */
42
+ endActivity(operationId: string): void;
43
+ /** Track an async operation automatically. */
44
+ trackOperation<T>(operationId: string, fn: () => Promise<T>): Promise<T>;
45
+ /** Get current activity states. */
46
+ getActivityStates(): ActivityStates;
47
+ }
48
+ //# sourceMappingURL=activity-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-tracker.d.ts","sourceRoot":"","sources":["../src/activity-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC;AAE1C,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAC5C,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,OAAO,CAAC,EAAE,sBAAsB;IAO5C,0DAA0D;IAC1D,kBAAkB,IAAI,IAAI;IAW1B,2EAA2E;IAC3E,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAYxC,wEAAwE;IACxE,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAatC,8CAA8C;IACxC,cAAc,CAAC,CAAC,EACpB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC;IASb,mCAAmC;IACnC,iBAAiB,IAAI,cAAc;CAUpC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Activity tracker for user vs CLI operation time tracking.
3
+ *
4
+ * Tracks overlapping operations with automatic deduplication.
5
+ * Injectable time source and callback for clean architecture.
6
+ *
7
+ * Extracted from Claude Code's ActivityManager — stripped singleton,
8
+ * bootstrap state, and metric counter coupling.
9
+ */
10
+ export class ActivityTracker {
11
+ activeOperations = new Set();
12
+ lastUserActivityTime = 0;
13
+ lastCliRecordedTime;
14
+ isCliActive = false;
15
+ getNow;
16
+ onRecord;
17
+ userTimeoutMs;
18
+ constructor(options) {
19
+ this.getNow = options?.getNow ?? (() => Date.now());
20
+ this.onRecord = options?.onRecord ?? (() => { });
21
+ this.userTimeoutMs = options?.userTimeoutMs ?? 5000;
22
+ this.lastCliRecordedTime = this.getNow();
23
+ }
24
+ /** Record a user interaction (typing, clicking, etc.). */
25
+ recordUserActivity() {
26
+ if (!this.isCliActive && this.lastUserActivityTime !== 0) {
27
+ const now = this.getNow();
28
+ const elapsed = (now - this.lastUserActivityTime) / 1000;
29
+ if (elapsed > 0 && elapsed < this.userTimeoutMs / 1000) {
30
+ this.onRecord({ type: "user", durationSeconds: elapsed });
31
+ }
32
+ }
33
+ this.lastUserActivityTime = this.getNow();
34
+ }
35
+ /** Start tracking a CLI operation. Duplicate IDs are force-ended first. */
36
+ startActivity(operationId) {
37
+ if (this.activeOperations.has(operationId)) {
38
+ this.endActivity(operationId);
39
+ }
40
+ const wasEmpty = this.activeOperations.size === 0;
41
+ this.activeOperations.add(operationId);
42
+ if (wasEmpty) {
43
+ this.isCliActive = true;
44
+ this.lastCliRecordedTime = this.getNow();
45
+ }
46
+ }
47
+ /** End tracking a CLI operation. Records CLI time when last op ends. */
48
+ endActivity(operationId) {
49
+ this.activeOperations.delete(operationId);
50
+ if (this.activeOperations.size === 0) {
51
+ const now = this.getNow();
52
+ const elapsed = (now - this.lastCliRecordedTime) / 1000;
53
+ if (elapsed > 0) {
54
+ this.onRecord({ type: "cli", durationSeconds: elapsed });
55
+ }
56
+ this.lastCliRecordedTime = now;
57
+ this.isCliActive = false;
58
+ }
59
+ }
60
+ /** Track an async operation automatically. */
61
+ async trackOperation(operationId, fn) {
62
+ this.startActivity(operationId);
63
+ try {
64
+ return await fn();
65
+ }
66
+ finally {
67
+ this.endActivity(operationId);
68
+ }
69
+ }
70
+ /** Get current activity states. */
71
+ getActivityStates() {
72
+ const now = this.getNow();
73
+ const elapsed = (now - this.lastUserActivityTime) / 1000;
74
+ return {
75
+ isUserActive: this.lastUserActivityTime > 0 && elapsed < this.userTimeoutMs / 1000,
76
+ isCliActive: this.isCliActive,
77
+ activeOperationCount: this.activeOperations.size,
78
+ };
79
+ }
80
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Analytics decoupling layer — deferred sink attachment with PII safety.
3
+ *
4
+ * ZERO external dependencies — pure domain layer.
5
+ *
6
+ * Inspired by Claude Code's analytics pattern:
7
+ * - Events queue before any sink is attached (startup race-free)
8
+ * - Drain happens via queueMicrotask (non-blocking)
9
+ * - PII-marked fields are stripped before forwarding to sink
10
+ * - _resetForTesting() ensures test isolation
11
+ */
12
+ /** A single analytics event. */
13
+ export interface AnalyticsEvent {
14
+ /** Machine-readable event name (e.g., "tool.invoked", "search.completed"). */
15
+ name: string;
16
+ /** Event properties. Keys prefixed with `_pii:` are stripped before sink. */
17
+ properties: Record<string, unknown>;
18
+ /** ISO timestamp — defaults to now. */
19
+ timestamp?: string;
20
+ }
21
+ /** Sink port — implement this to send events to your telemetry backend. */
22
+ export interface AnalyticsSink {
23
+ track(event: AnalyticsEvent): void;
24
+ flush?(): Promise<void>;
25
+ }
26
+ /**
27
+ * Analytics tracker with deferred sink attachment.
28
+ *
29
+ * Events are queued until a sink is attached, then drained via microtask.
30
+ * PII-marked properties are stripped before forwarding.
31
+ */
32
+ export declare class AnalyticsTracker {
33
+ private sink;
34
+ private queue;
35
+ private drainScheduled;
36
+ /** Attach a sink and drain any queued events. */
37
+ attach(sink: AnalyticsSink): void;
38
+ /** Track an event. If no sink is attached, queues for later delivery. */
39
+ track(name: string, properties?: Record<string, unknown>): void;
40
+ /** Flush the underlying sink (if it supports flushing). */
41
+ flush(): Promise<void>;
42
+ /** Reset all state — for test isolation only. */
43
+ _resetForTesting(): void;
44
+ private forward;
45
+ private scheduleDrain;
46
+ }
47
+ /** Default global tracker instance. */
48
+ export declare const analytics: AnalyticsTracker;
49
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAqBD;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,cAAc,CAAS;IAE/B,iDAAiD;IACjD,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAKjC,yEAAyE;IACzE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAanE,2DAA2D;IACrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,iDAAiD;IACjD,gBAAgB,IAAI,IAAI;IAMxB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,aAAa;CAWtB;AAID,uCAAuC;AACvC,eAAO,MAAM,SAAS,kBAAyB,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Analytics decoupling layer — deferred sink attachment with PII safety.
3
+ *
4
+ * ZERO external dependencies — pure domain layer.
5
+ *
6
+ * Inspired by Claude Code's analytics pattern:
7
+ * - Events queue before any sink is attached (startup race-free)
8
+ * - Drain happens via queueMicrotask (non-blocking)
9
+ * - PII-marked fields are stripped before forwarding to sink
10
+ * - _resetForTesting() ensures test isolation
11
+ */
12
+ // ─── PII Stripping ──────────────────────────────────────────────────
13
+ const PII_PREFIX = "_pii:";
14
+ /** Remove any property whose key starts with `_pii:` — returns a clean copy. */
15
+ function stripPii(properties) {
16
+ const clean = {};
17
+ for (const [key, value] of Object.entries(properties)) {
18
+ if (!key.startsWith(PII_PREFIX)) {
19
+ clean[key] = value;
20
+ }
21
+ }
22
+ return clean;
23
+ }
24
+ // ─── Tracker ────────────────────────────────────────────────────────
25
+ /**
26
+ * Analytics tracker with deferred sink attachment.
27
+ *
28
+ * Events are queued until a sink is attached, then drained via microtask.
29
+ * PII-marked properties are stripped before forwarding.
30
+ */
31
+ export class AnalyticsTracker {
32
+ sink = null;
33
+ queue = [];
34
+ drainScheduled = false;
35
+ /** Attach a sink and drain any queued events. */
36
+ attach(sink) {
37
+ this.sink = sink;
38
+ this.scheduleDrain();
39
+ }
40
+ /** Track an event. If no sink is attached, queues for later delivery. */
41
+ track(name, properties = {}) {
42
+ const event = {
43
+ name,
44
+ properties,
45
+ timestamp: new Date().toISOString(),
46
+ };
47
+ if (this.sink) {
48
+ this.forward(event);
49
+ }
50
+ else {
51
+ this.queue.push(event);
52
+ }
53
+ }
54
+ /** Flush the underlying sink (if it supports flushing). */
55
+ async flush() {
56
+ await this.sink?.flush?.();
57
+ }
58
+ /** Reset all state — for test isolation only. */
59
+ _resetForTesting() {
60
+ this.sink = null;
61
+ this.queue = [];
62
+ this.drainScheduled = false;
63
+ }
64
+ forward(event) {
65
+ if (!this.sink)
66
+ return; // Guard against drain after reset
67
+ const clean = {
68
+ ...event,
69
+ properties: stripPii(event.properties),
70
+ };
71
+ this.sink.track(clean);
72
+ }
73
+ scheduleDrain() {
74
+ if (this.drainScheduled || this.queue.length === 0)
75
+ return;
76
+ this.drainScheduled = true;
77
+ queueMicrotask(() => {
78
+ this.drainScheduled = false;
79
+ const events = this.queue.splice(0);
80
+ for (const event of events) {
81
+ this.forward(event);
82
+ }
83
+ });
84
+ }
85
+ }
86
+ // ─── Singleton ──────────────────────────────────────────────────────
87
+ /** Default global tracker instance. */
88
+ export const analytics = new AnalyticsTracker();
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Functional array utilities — zero dependencies.
3
+ *
4
+ * Inspired by Claude Code's `src/utils/array.ts`.
5
+ */
6
+ /** Interleave a separator (computed from index) between array elements. */
7
+ export declare function intersperse<A>(as: A[], separator: (index: number) => A): A[];
8
+ /** Count elements matching a predicate. */
9
+ export declare function count<T>(arr: readonly T[], pred: (x: T) => unknown): number;
10
+ /** Deduplicate via Set, preserving insertion order. */
11
+ export declare function uniq<T>(xs: Iterable<T>): T[];
12
+ //# sourceMappingURL=array.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../src/array.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE,CAE5E;AAED,2CAA2C;AAC3C,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,GAAG,MAAM,CAI3E;AAED,uDAAuD;AACvD,wBAAgB,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAE5C"}
package/dist/array.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Functional array utilities — zero dependencies.
3
+ *
4
+ * Inspired by Claude Code's `src/utils/array.ts`.
5
+ */
6
+ /** Interleave a separator (computed from index) between array elements. */
7
+ export function intersperse(as, separator) {
8
+ return as.flatMap((a, i) => (i ? [separator(i), a] : [a]));
9
+ }
10
+ /** Count elements matching a predicate. */
11
+ export function count(arr, pred) {
12
+ let n = 0;
13
+ for (const x of arr)
14
+ n += +!!pred(x);
15
+ return n;
16
+ }
17
+ /** Deduplicate via Set, preserving insertion order. */
18
+ export function uniq(xs) {
19
+ return [...new Set(xs)];
20
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * AsyncLocalStorage-based CWD override for concurrent async contexts.
3
+ *
4
+ * Enables multiple agents to each see their own working directory
5
+ * without affecting each other or the global process.cwd().
6
+ *
7
+ * Inspired by Claude Code's `src/utils/cwd.ts`.
8
+ */
9
+ /**
10
+ * Run a function with an overridden working directory for the current
11
+ * async context. All calls to `getContextCwd()` within `fn` (and its
12
+ * async descendants) will return `cwd`.
13
+ */
14
+ export declare function runWithCwdOverride<T>(cwd: string, fn: () => T): T;
15
+ /**
16
+ * Get the current working directory, respecting any active override.
17
+ * Falls back to `process.cwd()` when no override is set.
18
+ */
19
+ export declare function getContextCwd(): string;
20
+ //# sourceMappingURL=async-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-context.d.ts","sourceRoot":"","sources":["../src/async-context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEjE;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * AsyncLocalStorage-based CWD override for concurrent async contexts.
3
+ *
4
+ * Enables multiple agents to each see their own working directory
5
+ * without affecting each other or the global process.cwd().
6
+ *
7
+ * Inspired by Claude Code's `src/utils/cwd.ts`.
8
+ */
9
+ import { AsyncLocalStorage } from "async_hooks";
10
+ const cwdOverrideStorage = new AsyncLocalStorage();
11
+ /**
12
+ * Run a function with an overridden working directory for the current
13
+ * async context. All calls to `getContextCwd()` within `fn` (and its
14
+ * async descendants) will return `cwd`.
15
+ */
16
+ export function runWithCwdOverride(cwd, fn) {
17
+ return cwdOverrideStorage.run(cwd, fn);
18
+ }
19
+ /**
20
+ * Get the current working directory, respecting any active override.
21
+ * Falls back to `process.cwd()` when no override is set.
22
+ */
23
+ export function getContextCwd() {
24
+ return cwdOverrideStorage.getStore() ?? process.cwd();
25
+ }
package/dist/auth.d.ts CHANGED
@@ -39,6 +39,21 @@ export declare function resolveApiKey(): string;
39
39
  * Async version that tries token refresh before giving up.
40
40
  */
41
41
  export declare function resolveApiKeyWithRefresh(): Promise<string>;
42
+ export declare class McpTokenManager {
43
+ private refreshTimer;
44
+ /**
45
+ * Get a valid token, refreshing proactively if needed.
46
+ * This is the single entry point for auth — replaces resolveApiKey().
47
+ */
48
+ getValidToken(): Promise<string>;
49
+ /**
50
+ * Schedule a background refresh REFRESH_BEFORE_EXPIRY_S before token expires.
51
+ */
52
+ private scheduleRefresh;
53
+ private backgroundRefresh;
54
+ private clearTimer;
55
+ destroy(): void;
56
+ }
42
57
  export interface LoginResult {
43
58
  success: boolean;
44
59
  message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAWD,wBAAgB,qBAAqB,IAAI,iBAAiB,GAAG,IAAI,CAOhE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAa/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1D;AAID,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA0B7C;AAOD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOnD;AAID;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAuBtC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC,CAwBhE;AAuGD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,aAAa,CAAC,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAsCtB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAWD,wBAAgB,qBAAqB,IAAI,iBAAiB,GAAG,IAAI,CAOhE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAa/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1D;AAID,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA0B7C;AAOD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOnD;AAID;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAuBtC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC,CAwBhE;AASD,qBAAa,eAAe;IAC1B,OAAO,CAAC,YAAY,CAA8C;IAElE;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAsCtC;;OAEG;IACH,OAAO,CAAC,eAAe;YAeT,iBAAiB;IAQ/B,OAAO,CAAC,UAAU;IAOlB,OAAO,IAAI,IAAI;CAGhB;AAuGD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,aAAa,CAAC,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAsCtB"}
package/dist/auth.js CHANGED
@@ -191,6 +191,83 @@ export async function resolveApiKeyWithRefresh() {
191
191
  }
192
192
  return "";
193
193
  }
194
+ // ─── MCP Token Manager ──────────────────────────────────────────
195
+ // Proactive token refresh adapted for Node.js file-based credentials.
196
+ // Same pattern as @oka/auth-sdk TokenManager but reads ~/.oka/credentials.json
197
+ // instead of window.localStorage.
198
+ const REFRESH_BEFORE_EXPIRY_S = 300; // 5 minutes
199
+ export class McpTokenManager {
200
+ refreshTimer = null;
201
+ /**
202
+ * Get a valid token, refreshing proactively if needed.
203
+ * This is the single entry point for auth — replaces resolveApiKey().
204
+ */
205
+ async getValidToken() {
206
+ // 1. Environment variable always wins
207
+ const envKey = process.env["OKA_API_KEY"];
208
+ if (envKey)
209
+ return envKey;
210
+ // 2. Check stored credentials
211
+ const stored = loadStoredCredentials();
212
+ if (stored?.api_key)
213
+ return stored.api_key;
214
+ if (stored?.access_token) {
215
+ const now = Date.now() / 1000;
216
+ const exp = extractJwtExp(stored.access_token) ?? stored.expires_at ?? 0;
217
+ const expiringSoon = exp - now < REFRESH_BEFORE_EXPIRY_S;
218
+ if (!expiringSoon) {
219
+ this.scheduleRefresh(exp);
220
+ return stored.access_token;
221
+ }
222
+ // Token expired or expiring soon — refresh now
223
+ if (stored.refresh_token) {
224
+ const newToken = await tryRefresh();
225
+ if (newToken) {
226
+ const newExp = extractJwtExp(newToken) ?? 0;
227
+ this.scheduleRefresh(newExp);
228
+ return newToken;
229
+ }
230
+ }
231
+ // Refresh failed but token might still be technically valid
232
+ if (exp > now) {
233
+ return stored.access_token;
234
+ }
235
+ }
236
+ return "";
237
+ }
238
+ /**
239
+ * Schedule a background refresh REFRESH_BEFORE_EXPIRY_S before token expires.
240
+ */
241
+ scheduleRefresh(expiresAtSec) {
242
+ this.clearTimer();
243
+ const delayMs = (expiresAtSec - REFRESH_BEFORE_EXPIRY_S - Date.now() / 1000) * 1000;
244
+ if (delayMs <= 0)
245
+ return; // Already in refresh window, handled by getValidToken
246
+ this.refreshTimer = setTimeout(() => {
247
+ void this.backgroundRefresh();
248
+ }, delayMs);
249
+ // Don't let the timer prevent Node.js from exiting
250
+ if (this.refreshTimer && typeof this.refreshTimer === "object") {
251
+ this.refreshTimer.unref();
252
+ }
253
+ }
254
+ async backgroundRefresh() {
255
+ const newToken = await tryRefresh();
256
+ if (newToken) {
257
+ const exp = extractJwtExp(newToken) ?? 0;
258
+ this.scheduleRefresh(exp);
259
+ }
260
+ }
261
+ clearTimer() {
262
+ if (this.refreshTimer) {
263
+ clearTimeout(this.refreshTimer);
264
+ this.refreshTimer = null;
265
+ }
266
+ }
267
+ destroy() {
268
+ this.clearTimer();
269
+ }
270
+ }
194
271
  const DEFAULT_AUTH_SERVER = "https://id.oka.so";
195
272
  const DEFAULT_CLIENT_ID = "oka-reason-mcp";
196
273
  async function requestDeviceCode(authServerUrl, clientId) {
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Session-cached binary existence probe.
3
+ *
4
+ * Checks whether a command is installed via the platform's `which` / `where`.
5
+ * Results are cached for the process lifetime.
6
+ *
7
+ * Inspired by Claude Code's `src/utils/binaryCheck.ts`.
8
+ */
9
+ /**
10
+ * Check if a binary/command is installed and available on the system.
11
+ * Results are cached for the process lifetime.
12
+ */
13
+ export declare function isBinaryInstalled(command: string): Promise<boolean>;
14
+ /** Clear the binary check cache (useful for testing). */
15
+ export declare function clearBinaryCache(): void;
16
+ //# sourceMappingURL=binary-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary-check.d.ts","sourceRoot":"","sources":["../src/binary-check.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAoBH;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUzE;AAED,yDAAyD;AACzD,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Session-cached binary existence probe.
3
+ *
4
+ * Checks whether a command is installed via the platform's `which` / `where`.
5
+ * Results are cached for the process lifetime.
6
+ *
7
+ * Inspired by Claude Code's `src/utils/binaryCheck.ts`.
8
+ */
9
+ import { execFile } from "child_process";
10
+ const binaryCache = new Map();
11
+ /** Resolve a command to its path, or null if not found. */
12
+ function which(command) {
13
+ const cmd = process.platform === "win32" ? "where" : "which";
14
+ return new Promise((resolve) => {
15
+ execFile(cmd, [command], (err, stdout) => {
16
+ if (err) {
17
+ resolve(null);
18
+ }
19
+ else {
20
+ resolve(stdout.trim() || null);
21
+ }
22
+ });
23
+ });
24
+ }
25
+ /**
26
+ * Check if a binary/command is installed and available on the system.
27
+ * Results are cached for the process lifetime.
28
+ */
29
+ export async function isBinaryInstalled(command) {
30
+ if (!command || !command.trim())
31
+ return false;
32
+ const trimmed = command.trim();
33
+ const cached = binaryCache.get(trimmed);
34
+ if (cached !== undefined)
35
+ return cached;
36
+ const exists = (await which(trimmed)) !== null;
37
+ binaryCache.set(trimmed, exists);
38
+ return exists;
39
+ }
40
+ /** Clear the binary check cache (useful for testing). */
41
+ export function clearBinaryCache() {
42
+ binaryCache.clear();
43
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Batched I/O writer with backpressure — zero external dependencies.
3
+ *
4
+ * Batches writes by count, byte size, or time interval. Overflow batches
5
+ * are deferred via `setImmediate` to keep the current tick fast.
6
+ *
7
+ * Inspired by Claude Code's `src/utils/bufferedWriter.ts`.
8
+ */
9
+ export type BufferedWriter = {
10
+ /** Append content to the buffer. May trigger an async flush on overflow. */
11
+ write: (content: string) => void;
12
+ /** Flush all buffered content immediately. */
13
+ flush: () => void;
14
+ /** Flush and stop the periodic timer. */
15
+ dispose: () => void;
16
+ };
17
+ export type BufferedWriterOptions = {
18
+ /** The function that actually writes a batch of content. */
19
+ writeFn: (batch: string[]) => void;
20
+ /** Flush interval in ms. Default: 1000. */
21
+ flushIntervalMs?: number;
22
+ /** Max items before overflow flush. Default: 100. */
23
+ maxBufferSize?: number;
24
+ /** Max total bytes before overflow flush. Default: Infinity. */
25
+ maxBufferBytes?: number;
26
+ /** Bypass buffering entirely — write immediately. Default: false. */
27
+ immediateMode?: boolean;
28
+ };
29
+ export declare function createBufferedWriter(opts: BufferedWriterOptions): BufferedWriter;
30
+ //# sourceMappingURL=buffered-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffered-writer.d.ts","sourceRoot":"","sources":["../src/buffered-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,cAAc,GAAG;IAC3B,4EAA4E;IAC5E,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,8CAA8C;IAC9C,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,yCAAyC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,4DAA4D;IAC5D,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AASF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,qBAAqB,GAC1B,cAAc,CAsFhB"}