@fml-inc/panopticon 0.1.0

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 (124) hide show
  1. package/.claude-plugin/plugin.json +10 -0
  2. package/LICENSE +5 -0
  3. package/README.md +363 -0
  4. package/bin/hook-handler +3 -0
  5. package/bin/mcp-server +3 -0
  6. package/bin/panopticon +3 -0
  7. package/bin/proxy +3 -0
  8. package/bin/server +3 -0
  9. package/dist/api/client.d.ts +67 -0
  10. package/dist/api/client.js +48 -0
  11. package/dist/api/client.js.map +1 -0
  12. package/dist/chunk-3BUJ7URA.js +387 -0
  13. package/dist/chunk-3BUJ7URA.js.map +1 -0
  14. package/dist/chunk-3TZAKV3M.js +158 -0
  15. package/dist/chunk-3TZAKV3M.js.map +1 -0
  16. package/dist/chunk-4SM2H22C.js +169 -0
  17. package/dist/chunk-4SM2H22C.js.map +1 -0
  18. package/dist/chunk-7Q3BJMLG.js +62 -0
  19. package/dist/chunk-7Q3BJMLG.js.map +1 -0
  20. package/dist/chunk-BVOE7A2Z.js +412 -0
  21. package/dist/chunk-BVOE7A2Z.js.map +1 -0
  22. package/dist/chunk-CF4GPWLI.js +170 -0
  23. package/dist/chunk-CF4GPWLI.js.map +1 -0
  24. package/dist/chunk-DZ5HJFB4.js +467 -0
  25. package/dist/chunk-DZ5HJFB4.js.map +1 -0
  26. package/dist/chunk-HQCY722C.js +428 -0
  27. package/dist/chunk-HQCY722C.js.map +1 -0
  28. package/dist/chunk-HRCEIYKU.js +134 -0
  29. package/dist/chunk-HRCEIYKU.js.map +1 -0
  30. package/dist/chunk-K7YUPLES.js +76 -0
  31. package/dist/chunk-K7YUPLES.js.map +1 -0
  32. package/dist/chunk-L7G27XWF.js +130 -0
  33. package/dist/chunk-L7G27XWF.js.map +1 -0
  34. package/dist/chunk-LWXF7YRG.js +626 -0
  35. package/dist/chunk-LWXF7YRG.js.map +1 -0
  36. package/dist/chunk-NXH7AONS.js +1120 -0
  37. package/dist/chunk-NXH7AONS.js.map +1 -0
  38. package/dist/chunk-QK5442ZP.js +55 -0
  39. package/dist/chunk-QK5442ZP.js.map +1 -0
  40. package/dist/chunk-QVK6VGCV.js +1703 -0
  41. package/dist/chunk-QVK6VGCV.js.map +1 -0
  42. package/dist/chunk-RX2RXHBH.js +1699 -0
  43. package/dist/chunk-RX2RXHBH.js.map +1 -0
  44. package/dist/chunk-SEXU2WYG.js +788 -0
  45. package/dist/chunk-SEXU2WYG.js.map +1 -0
  46. package/dist/chunk-SUGSQ4YI.js +264 -0
  47. package/dist/chunk-SUGSQ4YI.js.map +1 -0
  48. package/dist/chunk-TGXFVAID.js +138 -0
  49. package/dist/chunk-TGXFVAID.js.map +1 -0
  50. package/dist/chunk-WLBNFVIG.js +447 -0
  51. package/dist/chunk-WLBNFVIG.js.map +1 -0
  52. package/dist/chunk-XLTCUH5A.js +1072 -0
  53. package/dist/chunk-XLTCUH5A.js.map +1 -0
  54. package/dist/chunk-YVRWVDIA.js +146 -0
  55. package/dist/chunk-YVRWVDIA.js.map +1 -0
  56. package/dist/chunk-ZEC4LRKS.js +176 -0
  57. package/dist/chunk-ZEC4LRKS.js.map +1 -0
  58. package/dist/cli.d.ts +1 -0
  59. package/dist/cli.js +1084 -0
  60. package/dist/cli.js.map +1 -0
  61. package/dist/config-NwoZC-GM.d.ts +20 -0
  62. package/dist/db.d.ts +46 -0
  63. package/dist/db.js +15 -0
  64. package/dist/db.js.map +1 -0
  65. package/dist/doctor.d.ts +37 -0
  66. package/dist/doctor.js +14 -0
  67. package/dist/doctor.js.map +1 -0
  68. package/dist/hooks/handler.d.ts +23 -0
  69. package/dist/hooks/handler.js +295 -0
  70. package/dist/hooks/handler.js.map +1 -0
  71. package/dist/index.d.ts +57 -0
  72. package/dist/index.js +101 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/mcp/server.d.ts +1 -0
  75. package/dist/mcp/server.js +243 -0
  76. package/dist/mcp/server.js.map +1 -0
  77. package/dist/otlp/server.d.ts +7 -0
  78. package/dist/otlp/server.js +17 -0
  79. package/dist/otlp/server.js.map +1 -0
  80. package/dist/permissions.d.ts +33 -0
  81. package/dist/permissions.js +14 -0
  82. package/dist/permissions.js.map +1 -0
  83. package/dist/pricing.d.ts +29 -0
  84. package/dist/pricing.js +13 -0
  85. package/dist/pricing.js.map +1 -0
  86. package/dist/proxy/server.d.ts +10 -0
  87. package/dist/proxy/server.js +20 -0
  88. package/dist/proxy/server.js.map +1 -0
  89. package/dist/prune.d.ts +18 -0
  90. package/dist/prune.js +13 -0
  91. package/dist/prune.js.map +1 -0
  92. package/dist/query.d.ts +56 -0
  93. package/dist/query.js +27 -0
  94. package/dist/query.js.map +1 -0
  95. package/dist/reparse-636YZCE3.js +14 -0
  96. package/dist/reparse-636YZCE3.js.map +1 -0
  97. package/dist/repo.d.ts +17 -0
  98. package/dist/repo.js +9 -0
  99. package/dist/repo.js.map +1 -0
  100. package/dist/scanner.d.ts +73 -0
  101. package/dist/scanner.js +15 -0
  102. package/dist/scanner.js.map +1 -0
  103. package/dist/sdk.d.ts +82 -0
  104. package/dist/sdk.js +208 -0
  105. package/dist/sdk.js.map +1 -0
  106. package/dist/server.d.ts +5 -0
  107. package/dist/server.js +25 -0
  108. package/dist/server.js.map +1 -0
  109. package/dist/setup.d.ts +35 -0
  110. package/dist/setup.js +19 -0
  111. package/dist/setup.js.map +1 -0
  112. package/dist/sync/index.d.ts +29 -0
  113. package/dist/sync/index.js +32 -0
  114. package/dist/sync/index.js.map +1 -0
  115. package/dist/targets.d.ts +279 -0
  116. package/dist/targets.js +20 -0
  117. package/dist/targets.js.map +1 -0
  118. package/dist/types-D-MYCBol.d.ts +128 -0
  119. package/dist/types.d.ts +164 -0
  120. package/dist/types.js +1 -0
  121. package/dist/types.js.map +1 -0
  122. package/hooks/hooks.json +274 -0
  123. package/package.json +124 -0
  124. package/skills/panopticon-optimize/SKILL.md +222 -0
package/dist/setup.js ADDED
@@ -0,0 +1,19 @@
1
+ import {
2
+ configureShellEnv,
3
+ fetchPricing,
4
+ initDb
5
+ } from "./chunk-L7G27XWF.js";
6
+ import "./chunk-3TZAKV3M.js";
7
+ import "./chunk-ZEC4LRKS.js";
8
+ import "./chunk-QVK6VGCV.js";
9
+ import "./chunk-DZ5HJFB4.js";
10
+ import {
11
+ config
12
+ } from "./chunk-K7YUPLES.js";
13
+ export {
14
+ config,
15
+ configureShellEnv,
16
+ fetchPricing,
17
+ initDb
18
+ };
19
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,29 @@
1
+ import { S as SyncTarget, a as SyncFilter, b as SyncOptions, c as SyncHandle, T as TableSyncDescriptor } from '../types-D-MYCBol.js';
2
+ export { R as RepoConfigSnapshotRecord, d as SessionSyncRecord, U as UserConfigSnapshotRecord } from '../types-D-MYCBol.js';
3
+
4
+ interface SyncConfig {
5
+ targets: SyncTarget[];
6
+ filter?: SyncFilter;
7
+ }
8
+ declare function loadSyncConfig(): SyncConfig;
9
+ declare function saveSyncConfig(syncCfg: SyncConfig): void;
10
+ declare function addTarget(target: SyncTarget): void;
11
+ declare function removeTarget(name: string): boolean;
12
+ declare function listTargets(): SyncTarget[];
13
+
14
+ declare function createSyncLoop(opts: SyncOptions): SyncHandle;
15
+
16
+ /**
17
+ * Ordered list of table sync descriptors. The order matches the
18
+ * round-robin execution order in the sync loop.
19
+ *
20
+ * All tables sync via POST /v1/sync with {table, rows} payload.
21
+ * Session-linked tables are filtered by repo attribution in the sync loop.
22
+ */
23
+ declare const TABLE_SYNC_REGISTRY: TableSyncDescriptor<any>[];
24
+
25
+ declare function watermarkKey(table: string, targetName: string): string;
26
+ declare function readWatermark(key: string): number;
27
+ declare function resetWatermarks(targetName?: string): void;
28
+
29
+ export { SyncFilter, SyncHandle, SyncOptions, SyncTarget, TABLE_SYNC_REGISTRY, TableSyncDescriptor, addTarget, createSyncLoop, listTargets, loadSyncConfig, readWatermark, removeTarget, resetWatermarks, saveSyncConfig, watermarkKey };
@@ -0,0 +1,32 @@
1
+ import {
2
+ addTarget,
3
+ createSyncLoop,
4
+ listTargets,
5
+ loadSyncConfig,
6
+ removeTarget,
7
+ saveSyncConfig
8
+ } from "../chunk-HQCY722C.js";
9
+ import "../chunk-CF4GPWLI.js";
10
+ import "../chunk-7Q3BJMLG.js";
11
+ import {
12
+ TABLE_SYNC_REGISTRY,
13
+ readWatermark,
14
+ resetWatermarks,
15
+ watermarkKey
16
+ } from "../chunk-SEXU2WYG.js";
17
+ import "../chunk-QK5442ZP.js";
18
+ import "../chunk-DZ5HJFB4.js";
19
+ import "../chunk-K7YUPLES.js";
20
+ export {
21
+ TABLE_SYNC_REGISTRY,
22
+ addTarget,
23
+ createSyncLoop,
24
+ listTargets,
25
+ loadSyncConfig,
26
+ readWatermark,
27
+ removeTarget,
28
+ resetWatermarks,
29
+ saveSyncConfig,
30
+ watermarkKey
31
+ };
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,279 @@
1
+ interface HookInput {
2
+ session_id: string;
3
+ hook_event_name: string;
4
+ cwd?: string;
5
+ repository?: string;
6
+ tool_name?: string;
7
+ tool_input?: Record<string, unknown>;
8
+ source?: string;
9
+ target?: string;
10
+ prompt?: string;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ /**
15
+ * Target adapter types — each supported coding tool declares its specifics
16
+ * via this interface so consumers can iterate over the registry instead of
17
+ * hardcoding target-specific branches.
18
+ */
19
+
20
+ /**
21
+ * All hook event names panopticon registers for.
22
+ *
23
+ * Claude Code fires these as shell commands via hooks.json — each invocation
24
+ * pipes a JSON payload to stdin with session_id, hook_event_name, and
25
+ * event-specific fields. Panopticon's hook-handler POSTs the payload to
26
+ * the local server, which calls processHookEvent() in ingest.ts.
27
+ *
28
+ * Non-Claude targets (Gemini, Codex) map their native event names to these
29
+ * canonical names via their adapter's eventMap.
30
+ */
31
+ declare const ALL_EVENTS: readonly ["SessionStart", "SessionEnd", "Setup", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PostToolUseFailure", "PermissionRequest", "PermissionDenied", "Stop", "StopFailure", "SubagentStart", "SubagentStop", "PreCompact", "PostCompact", "Notification", "TeammateIdle", "TaskCreated", "TaskCompleted", "Elicitation", "ElicitationResult", "ConfigChange", "InstructionsLoaded", "CwdChanged", "FileChanged", "WorktreeCreate", "WorktreeRemove"];
32
+ /** Union type of all supported event names. */
33
+ type CanonicalEvent = (typeof ALL_EVENTS)[number];
34
+ interface TargetConfigSpec {
35
+ /** Directory where this target stores its config, e.g. ~/.claude */
36
+ dir: string;
37
+ /** Path to the main config file */
38
+ configPath: string;
39
+ /** Format of the config file */
40
+ configFormat: "json" | "toml";
41
+ }
42
+ interface TargetInstallOpts {
43
+ pluginRoot: string;
44
+ port: number;
45
+ proxy?: boolean;
46
+ }
47
+ interface TargetHookSpec {
48
+ /** Event names this target uses, in the target's own convention */
49
+ events: string[];
50
+ /**
51
+ * Apply panopticon hook registration (and related config like MCP servers,
52
+ * telemetry) to this target's existing config. Each target handles its own
53
+ * deduplication/merge logic. Returns the modified config.
54
+ */
55
+ applyInstallConfig(existingConfig: Record<string, unknown>, opts: TargetInstallOpts): Record<string, unknown>;
56
+ /**
57
+ * Remove panopticon hook registration (and related config like MCP servers,
58
+ * telemetry) from this target's existing config. Returns the modified config.
59
+ */
60
+ removeInstallConfig(existingConfig: Record<string, unknown>): Record<string, unknown>;
61
+ }
62
+ interface TargetShellEnvSpec {
63
+ /**
64
+ * Env vars to export as [varName, value] tuples.
65
+ * These are target-specific; shared OTEL_* vars are handled separately.
66
+ */
67
+ envVars(port: number, proxy: boolean): Array<[string, string]>;
68
+ }
69
+ interface TargetEventSpec {
70
+ /** Map from target's event name to canonical panopticon event name. */
71
+ eventMap: Record<string, CanonicalEvent>;
72
+ /**
73
+ * Transform the raw hook payload before storage.
74
+ * Used e.g. by Gemini to extract user_prompt from llm_request.messages.
75
+ */
76
+ normalizePayload?(data: HookInput): HookInput;
77
+ /**
78
+ * Format a permission response for this target's expected shape.
79
+ */
80
+ formatPermissionResponse(decision: {
81
+ allow: boolean;
82
+ reason: string;
83
+ }): Record<string, unknown>;
84
+ }
85
+ interface TargetDetectSpec {
86
+ /** Human-readable name for display, e.g. "Claude Code" */
87
+ displayName: string;
88
+ /** Check whether this target is installed on the system */
89
+ isInstalled(): boolean;
90
+ /** Check whether panopticon is configured within this target */
91
+ isConfigured(): boolean;
92
+ }
93
+ interface MetricSpec {
94
+ /** OTel metric name(s) this target emits for token usage */
95
+ metricNames: string[];
96
+ /** Aggregation function: 'SUM' for per-request deltas, 'MAX' for cumulative counters */
97
+ aggregation: "SUM" | "MAX";
98
+ /** JSON paths to extract token type from metric attributes (first non-null wins) */
99
+ tokenTypeAttrs: string[];
100
+ /** JSON paths to extract model name from metric attributes (first non-null wins) */
101
+ modelAttrs: string[];
102
+ /** Remap token_type values before aggregation, e.g. { cached_input: 'cacheRead' } */
103
+ tokenTypeMap?: Record<string, string>;
104
+ /** Token type values to exclude (e.g. 'total' to avoid double-counting) */
105
+ excludeTokenTypes?: string[];
106
+ }
107
+ interface OtelLogFieldSpec {
108
+ /** SQL expressions to extract event type from otel_logs (COALESCEd). Default: ['body'] */
109
+ eventTypeExprs?: string[];
110
+ /** SQL expressions to extract timestamp in ms from otel_logs. Default: ['CAST(timestamp_ns / 1000000 AS INTEGER)'] */
111
+ timestampMsExprs?: string[];
112
+ }
113
+ interface TargetOtelSpec {
114
+ /** OTel service.name this target emits. Used for session inference when metrics lack session_id. */
115
+ serviceName?: string;
116
+ /** Token usage metric declaration. Undefined means no token metrics. */
117
+ metrics?: MetricSpec;
118
+ /** How to extract event type and timestamp from otel_logs rows. */
119
+ logFields?: OtelLogFieldSpec;
120
+ }
121
+ interface TargetIdentSpec {
122
+ /** Model-name regex patterns for last-resort target identification from hook payloads */
123
+ modelPatterns?: RegExp[];
124
+ }
125
+ interface TargetProxySpec {
126
+ /**
127
+ * Upstream host for API proxying.
128
+ * String for simple mapping; function for dynamic routing (e.g. Codex JWT).
129
+ */
130
+ upstreamHost: string | ((headers: Record<string, string>) => string);
131
+ /** Path rewrite rule, if needed. Default: pass through. */
132
+ rewritePath?(path: string, headers: Record<string, string>): string;
133
+ /** Which stream accumulator to use */
134
+ accumulatorType: "anthropic" | "openai";
135
+ }
136
+ interface DiscoveredFile {
137
+ filePath: string;
138
+ }
139
+ interface ParsedTurn {
140
+ sessionId: string;
141
+ turnIndex: number;
142
+ timestampMs: number;
143
+ model?: string;
144
+ role: "user" | "assistant";
145
+ contentPreview?: string;
146
+ inputTokens: number;
147
+ outputTokens: number;
148
+ cacheReadTokens: number;
149
+ cacheCreationTokens: number;
150
+ reasoningTokens: number;
151
+ }
152
+ type RelationshipType = "subagent" | "continuation" | "fork";
153
+ interface ParsedSession {
154
+ sessionId: string;
155
+ parentSessionId?: string;
156
+ relationshipType?: RelationshipType;
157
+ model?: string;
158
+ cwd?: string;
159
+ cliVersion?: string;
160
+ startedAtMs?: number;
161
+ firstPrompt?: string;
162
+ }
163
+ interface ParsedEvent {
164
+ sessionId: string;
165
+ eventType: string;
166
+ timestampMs: number;
167
+ toolName?: string;
168
+ toolInput?: string;
169
+ toolOutput?: string;
170
+ content?: string;
171
+ metadata?: Record<string, unknown>;
172
+ }
173
+ interface ParsedToolCall {
174
+ toolUseId: string;
175
+ toolName: string;
176
+ category: string;
177
+ inputJson?: string;
178
+ skillName?: string;
179
+ resultContentLength?: number;
180
+ resultContent?: string;
181
+ subagentSessionId?: string;
182
+ /** Timestamp (ms) when the tool was invoked (from the assistant message). */
183
+ timestampMs?: number;
184
+ }
185
+ interface ParsedMessage {
186
+ sessionId: string;
187
+ ordinal: number;
188
+ role: "user" | "assistant";
189
+ content: string;
190
+ timestampMs?: number;
191
+ hasThinking: boolean;
192
+ hasToolUse: boolean;
193
+ isSystem: boolean;
194
+ contentLength: number;
195
+ model?: string;
196
+ tokenUsage?: string;
197
+ contextTokens?: number;
198
+ outputTokens?: number;
199
+ hasContextTokens: boolean;
200
+ hasOutputTokens: boolean;
201
+ /** DAG node UUID from the JSONL line that produced this message. */
202
+ uuid?: string;
203
+ /** Parent DAG node UUID (the line this message is a reply to). */
204
+ parentUuid?: string;
205
+ toolCalls: ParsedToolCall[];
206
+ /** tool_use_id → raw result content (from tool_result blocks in user messages) */
207
+ toolResults: Map<string, {
208
+ contentLength: number;
209
+ contentRaw: string;
210
+ timestampMs?: number;
211
+ }>;
212
+ }
213
+ interface ParseResult {
214
+ meta?: ParsedSession;
215
+ turns: ParsedTurn[];
216
+ events: ParsedEvent[];
217
+ messages: ParsedMessage[];
218
+ newByteOffset: number;
219
+ /**
220
+ * When true, turn indices are absolute (0-based from start of session)
221
+ * and the caller should NOT re-index them. Used by parsers that re-read
222
+ * the full file (e.g. Gemini JSON) rather than reading incrementally.
223
+ * INSERT OR IGNORE handles dedup via the UNIQUE constraint.
224
+ */
225
+ absoluteIndices?: boolean;
226
+ /** Additional sessions from DAG fork detection (branched conversations). */
227
+ forks?: ParseResult[];
228
+ /**
229
+ * When true, the parser detected a DAG fork during incremental reading.
230
+ * The caller should reset the file watermark and reparse from byte 0
231
+ * so fork detection can run on the full file.
232
+ */
233
+ needsFullReparse?: boolean;
234
+ /**
235
+ * Tool results from filtered-out messages (e.g. tool-result-only user
236
+ * messages) that still need to be backfilled into tool_calls.
237
+ */
238
+ orphanedToolResults?: Map<string, {
239
+ contentLength: number;
240
+ contentRaw: string;
241
+ timestampMs?: number;
242
+ }>;
243
+ }
244
+ interface TargetScannerSpec {
245
+ /** Discover session files on disk for this target. */
246
+ discover(): DiscoveredFile[];
247
+ /**
248
+ * Parse a session file. Receives the file path and current byte offset.
249
+ * Returns parsed data and new byte offset, or null if no new data.
250
+ */
251
+ parseFile(filePath: string, fromByteOffset: number): ParseResult | null;
252
+ /** Normalize a tool name to a standard category for analytics grouping. */
253
+ normalizeToolCategory(toolName: string): string;
254
+ }
255
+ interface TargetAdapter {
256
+ /** Machine identifier: "claude", "gemini", "codex", etc. */
257
+ id: string;
258
+ config: TargetConfigSpec;
259
+ hooks: TargetHookSpec;
260
+ shellEnv: TargetShellEnvSpec;
261
+ events: TargetEventSpec;
262
+ detect: TargetDetectSpec;
263
+ /** Proxy spec is optional — not every target routes through the proxy */
264
+ proxy?: TargetProxySpec;
265
+ /** OTel telemetry schema — how this target emits metrics and logs */
266
+ otel?: TargetOtelSpec;
267
+ /** How to identify this target from hook payloads when no explicit source field is present */
268
+ ident?: TargetIdentSpec;
269
+ /** Session file scanner — reads local transcript files for token usage */
270
+ scanner?: TargetScannerSpec;
271
+ }
272
+
273
+ declare function registerTarget(adapter: TargetAdapter): void;
274
+ declare function getTarget(id: string): TargetAdapter | undefined;
275
+ declare function getTargetOrThrow(id: string): TargetAdapter;
276
+ declare function allTargets(): TargetAdapter[];
277
+ declare function targetIds(): string[];
278
+
279
+ export { ALL_EVENTS, type CanonicalEvent, type TargetAdapter, type TargetConfigSpec, type TargetDetectSpec, type TargetEventSpec, type TargetHookSpec, type TargetInstallOpts, type TargetProxySpec, type TargetShellEnvSpec, allTargets, getTarget, getTargetOrThrow, registerTarget, targetIds };
@@ -0,0 +1,20 @@
1
+ import {
2
+ ALL_EVENTS
3
+ } from "./chunk-ZEC4LRKS.js";
4
+ import {
5
+ allTargets,
6
+ getTarget,
7
+ getTargetOrThrow,
8
+ registerTarget,
9
+ targetIds
10
+ } from "./chunk-QVK6VGCV.js";
11
+ import "./chunk-K7YUPLES.js";
12
+ export {
13
+ ALL_EVENTS,
14
+ allTargets,
15
+ getTarget,
16
+ getTargetOrThrow,
17
+ registerTarget,
18
+ targetIds
19
+ };
20
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,128 @@
1
+ interface SyncTarget {
2
+ /** Unique name for this target, used as watermark namespace */
3
+ name: string;
4
+ /** Sync endpoint base URL (e.g. "https://example.com") */
5
+ url: string;
6
+ /** Bearer token — sent as Authorization: Bearer <token> */
7
+ token?: string;
8
+ /** Shell command that returns a Bearer token on stdout (e.g. "gh auth token"). Cached for 5 minutes. */
9
+ tokenCommand?: string;
10
+ /** Additional headers (merged with token auth if both provided) */
11
+ headers?: Record<string, string>;
12
+ }
13
+ interface SyncFilter {
14
+ /** Only sync events matching these repo patterns (glob, e.g. "fml-inc/*") */
15
+ includeRepos?: string[];
16
+ /** Exclude repos matching these patterns (takes precedence over include) */
17
+ excludeRepos?: string[];
18
+ /** Only sync sessions with associated repo attribution (default true) */
19
+ requireRepo?: boolean;
20
+ }
21
+ interface SyncOptions {
22
+ targets: SyncTarget[];
23
+ filter?: SyncFilter;
24
+ /** Max rows read from SQLite per batch (default 2000) */
25
+ batchSize?: number;
26
+ /** Max records per HTTP POST (default 25) */
27
+ postBatchSize?: number;
28
+ /** If true, timer keeps Node alive (default false) */
29
+ keepAlive?: boolean;
30
+ /** Idle poll interval in ms (default 30000) */
31
+ idleIntervalMs?: number;
32
+ /** Catch-up poll interval in ms (default 1000) */
33
+ catchUpIntervalMs?: number;
34
+ }
35
+ interface SyncHandle {
36
+ start: () => void;
37
+ stop: () => void;
38
+ }
39
+ interface UserConfigSnapshotRecord {
40
+ id: number;
41
+ deviceName: string;
42
+ snapshotAtMs: number;
43
+ contentHash: string;
44
+ permissions: Record<string, unknown>;
45
+ enabledPlugins: unknown[];
46
+ hooks: unknown[];
47
+ commands: unknown[];
48
+ rules: unknown[];
49
+ skills: unknown[];
50
+ }
51
+ interface RepoConfigSnapshotRecord {
52
+ id: number;
53
+ repository: string;
54
+ cwd: string;
55
+ sessionId: string | null;
56
+ snapshotAtMs: number;
57
+ contentHash: string;
58
+ hooks: unknown[];
59
+ mcpServers: unknown[];
60
+ commands: unknown[];
61
+ agents: unknown[];
62
+ rules: unknown[];
63
+ localHooks: unknown[];
64
+ localMcpServers: unknown[];
65
+ localPermissions: Record<string, unknown>;
66
+ localIsGitignored: boolean;
67
+ instructions: unknown[];
68
+ }
69
+ interface SessionSyncRecord {
70
+ sessionId: string;
71
+ target: string | null;
72
+ startedAtMs: number | null;
73
+ endedAtMs: number | null;
74
+ cwd: string | null;
75
+ firstPrompt: string | null;
76
+ permissionMode: string | null;
77
+ agentVersion: string | null;
78
+ totalInputTokens: number | null;
79
+ totalOutputTokens: number | null;
80
+ totalCacheReadTokens: number | null;
81
+ totalCacheCreationTokens: number | null;
82
+ totalReasoningTokens: number | null;
83
+ turnCount: number | null;
84
+ models: string | null;
85
+ summary: string | null;
86
+ toolCounts: Record<string, number>;
87
+ hookToolCounts: Record<string, number>;
88
+ eventTypeCounts: Record<string, number>;
89
+ hookEventTypeCounts: Record<string, number>;
90
+ project: string | null;
91
+ machine: string;
92
+ messageCount: number;
93
+ userMessageCount: number;
94
+ parentSessionId: string | null;
95
+ relationshipType: string;
96
+ isAutomated: boolean;
97
+ createdAt: number | null;
98
+ repositories: Array<{
99
+ repository: string;
100
+ firstSeenMs: number;
101
+ gitUserName: string | null;
102
+ gitUserEmail: string | null;
103
+ branch: string | null;
104
+ }>;
105
+ cwds: Array<{
106
+ cwd: string;
107
+ firstSeenMs: number;
108
+ }>;
109
+ }
110
+ /**
111
+ * Descriptor for a single table that participates in the sync loop.
112
+ * All tables POST to /v1/sync with {table, rows} payload.
113
+ */
114
+ interface TableSyncDescriptor<TRow = unknown> {
115
+ /** Table name, used as watermark key and sent in the POST body. */
116
+ table: string;
117
+ /** Noun for log messages (e.g. "events", "logs"). */
118
+ logNoun: string;
119
+ /** Read rows from SQLite starting after the given watermark. */
120
+ read: (afterId: number, limit: number) => {
121
+ rows: TRow[];
122
+ maxId: number;
123
+ };
124
+ /** Whether this table's rows are linked to sessions (filtered by repo). */
125
+ sessionLinked: boolean;
126
+ }
127
+
128
+ export type { RepoConfigSnapshotRecord as R, SyncTarget as S, TableSyncDescriptor as T, UserConfigSnapshotRecord as U, SyncFilter as a, SyncOptions as b, SyncHandle as c, SessionSyncRecord as d };
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Unified response types for panopticon query functions.
3
+ *
4
+ * These types define the canonical shapes returned by both local queries
5
+ * (panopticon SQLite) and remote queries (FML backend). Both sources
6
+ * populate the same fields — the data originates from panopticon either way.
7
+ *
8
+ * Conventions:
9
+ * - camelCase field names
10
+ * - ISO 8601 strings for timestamps
11
+ * - null for genuinely absent data, never for source-dependent gaps
12
+ */
13
+ interface Repository {
14
+ name: string;
15
+ gitUserName: string | null;
16
+ gitUserEmail: string | null;
17
+ }
18
+ interface Session {
19
+ sessionId: string;
20
+ target: string | null;
21
+ model: string | null;
22
+ project: string | null;
23
+ startedAt: string | null;
24
+ endedAt: string | null;
25
+ firstPrompt: string | null;
26
+ turnCount: number;
27
+ messageCount: number;
28
+ totalInputTokens: number;
29
+ totalOutputTokens: number;
30
+ totalCost: number;
31
+ repositories: Repository[];
32
+ parentSessionId: string | null;
33
+ relationshipType: string | null;
34
+ summary: string | null;
35
+ }
36
+ interface SessionListResult {
37
+ sessions: Session[];
38
+ totalCount: number;
39
+ source: "local" | "remote";
40
+ }
41
+ interface TimelineToolCall {
42
+ toolName: string;
43
+ category: string;
44
+ toolUseId: string | null;
45
+ inputJson: string | null;
46
+ skillName: string | null;
47
+ resultContentLength: number | null;
48
+ durationMs: number | null;
49
+ subagentSessionId: string | null;
50
+ /** Subagent session metadata, present when subagentSessionId is set */
51
+ subagent: {
52
+ sessionId: string;
53
+ model: string | null;
54
+ turnCount: number;
55
+ firstPrompt: string | null;
56
+ } | null;
57
+ }
58
+ interface TimelineMessage {
59
+ id: number;
60
+ ordinal: number;
61
+ role: string;
62
+ content: string;
63
+ timestampMs: number | null;
64
+ model: string | null;
65
+ isSystem: boolean;
66
+ hasThinking: boolean;
67
+ hasToolUse: boolean;
68
+ contentLength: number;
69
+ uuid: string | null;
70
+ parentUuid: string | null;
71
+ tokenUsage: string | null;
72
+ contextTokens: number;
73
+ outputTokens: number;
74
+ toolCalls: TimelineToolCall[];
75
+ }
76
+ interface ChildSession {
77
+ sessionId: string;
78
+ relationshipType: string;
79
+ model: string | null;
80
+ turnCount: number;
81
+ firstPrompt: string | null;
82
+ startedAtMs: number | null;
83
+ }
84
+ interface SessionTimelineResult {
85
+ session: {
86
+ sessionId: string;
87
+ target: string | null;
88
+ model: string | null;
89
+ project: string | null;
90
+ parentSessionId: string | null;
91
+ relationshipType: string | null;
92
+ repositories: Repository[];
93
+ childSessions: ChildSession[];
94
+ } | null;
95
+ messages: TimelineMessage[];
96
+ totalMessages: number;
97
+ hasMore: boolean;
98
+ source: "local" | "remote";
99
+ }
100
+ interface SpendingGroup {
101
+ key: string;
102
+ inputTokens: number;
103
+ outputTokens: number;
104
+ totalTokens: number;
105
+ totalCost: number;
106
+ sessionCount: number;
107
+ }
108
+ interface SpendingResult {
109
+ groups: SpendingGroup[];
110
+ totals: {
111
+ inputTokens: number;
112
+ outputTokens: number;
113
+ totalTokens: number;
114
+ totalCost: number;
115
+ };
116
+ groupBy: "session" | "model" | "day";
117
+ source: "local" | "remote";
118
+ }
119
+ interface ActivitySessionDetail {
120
+ sessionId: string;
121
+ startedAt: string | null;
122
+ durationMinutes: number;
123
+ model: string | null;
124
+ project: string | null;
125
+ repositories: Repository[];
126
+ userPrompts: string[];
127
+ toolsUsed: Array<{
128
+ tool: string;
129
+ count: number;
130
+ }>;
131
+ filesModified: string[];
132
+ totalCost: number;
133
+ }
134
+ interface ActivitySummaryResult {
135
+ period: {
136
+ since: string;
137
+ until: string;
138
+ };
139
+ totalSessions: number;
140
+ totalTokens: number;
141
+ totalCost: number;
142
+ topTools: Array<{
143
+ tool: string;
144
+ count: number;
145
+ }>;
146
+ sessions: ActivitySessionDetail[];
147
+ source: "local" | "remote";
148
+ }
149
+ interface SearchMatch {
150
+ sessionId: string;
151
+ timestamp: string;
152
+ matchType: string;
153
+ matchSnippet: string;
154
+ eventType: string | null;
155
+ toolName: string | null;
156
+ }
157
+ interface SearchResult {
158
+ results: SearchMatch[];
159
+ totalMatches: number;
160
+ query: string;
161
+ source: "local" | "remote";
162
+ }
163
+
164
+ export type { ActivitySessionDetail, ActivitySummaryResult, ChildSession, Repository, SearchMatch, SearchResult, Session, SessionListResult, SessionTimelineResult, SpendingGroup, SpendingResult, TimelineMessage, TimelineToolCall };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}