@ekkos/cli 0.2.9 → 0.2.11

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 (44) hide show
  1. package/dist/agent/daemon.d.ts +86 -0
  2. package/dist/agent/daemon.js +297 -0
  3. package/dist/agent/pty-runner.d.ts +51 -0
  4. package/dist/agent/pty-runner.js +184 -0
  5. package/dist/cache/LocalSessionStore.d.ts +34 -21
  6. package/dist/cache/LocalSessionStore.js +169 -53
  7. package/dist/cache/capture.d.ts +19 -11
  8. package/dist/cache/capture.js +243 -76
  9. package/dist/cache/types.d.ts +14 -1
  10. package/dist/commands/agent.d.ts +44 -0
  11. package/dist/commands/agent.js +300 -0
  12. package/dist/commands/doctor.d.ts +10 -0
  13. package/dist/commands/doctor.js +175 -87
  14. package/dist/commands/hooks.d.ts +109 -0
  15. package/dist/commands/hooks.js +668 -0
  16. package/dist/commands/run.d.ts +2 -0
  17. package/dist/commands/run.js +357 -85
  18. package/dist/commands/setup-remote.d.ts +20 -0
  19. package/dist/commands/setup-remote.js +467 -0
  20. package/dist/index.js +116 -1
  21. package/dist/restore/RestoreOrchestrator.d.ts +17 -3
  22. package/dist/restore/RestoreOrchestrator.js +64 -22
  23. package/dist/utils/paths.d.ts +125 -0
  24. package/dist/utils/paths.js +283 -0
  25. package/dist/utils/state.d.ts +2 -0
  26. package/package.json +1 -1
  27. package/templates/ekkos-manifest.json +223 -0
  28. package/templates/helpers/json-parse.cjs +101 -0
  29. package/templates/hooks/assistant-response.ps1 +256 -0
  30. package/templates/hooks/assistant-response.sh +124 -64
  31. package/templates/hooks/session-start.ps1 +107 -2
  32. package/templates/hooks/session-start.sh +201 -166
  33. package/templates/hooks/stop.ps1 +124 -3
  34. package/templates/hooks/stop.sh +470 -843
  35. package/templates/hooks/user-prompt-submit.ps1 +107 -22
  36. package/templates/hooks/user-prompt-submit.sh +403 -393
  37. package/templates/project-stubs/session-start.ps1 +63 -0
  38. package/templates/project-stubs/session-start.sh +55 -0
  39. package/templates/project-stubs/stop.ps1 +63 -0
  40. package/templates/project-stubs/stop.sh +55 -0
  41. package/templates/project-stubs/user-prompt-submit.ps1 +63 -0
  42. package/templates/project-stubs/user-prompt-submit.sh +55 -0
  43. package/templates/shared/hooks-enabled.json +22 -0
  44. package/templates/shared/session-words.json +45 -0
@@ -1,12 +1,17 @@
1
1
  "use strict";
2
2
  /**
3
- * ekkOS Fast /continue - Restore Orchestrator
3
+ * ekkOS Fast /continue - Restore Orchestrator (Instance-Aware)
4
4
  *
5
5
  * Implements the 3-tier restore chain for near-zero context loss:
6
+ * - Tier -1: Stream log (real-time, has mid-turn content)
6
7
  * - Tier 0: Local JSONL cache (~20ms)
7
8
  * - Tier 1: Redis hot cache (~150ms)
8
9
  * - Tier 2: Supabase cold store (~500ms)
9
10
  *
11
+ * Per ekkOS Onboarding Spec v1.2 FINAL + ADDENDUM:
12
+ * - All Tier 0 cache paths MUST be: ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.jsonl
13
+ * - All persisted records MUST include: instanceId, sessionId, sessionName
14
+ *
10
15
  * Falls back through tiers on miss, tracks which tier succeeded.
11
16
  */
12
17
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
@@ -44,16 +49,17 @@ var __importStar = (this && this.__importStar) || (function () {
44
49
  })();
45
50
  Object.defineProperty(exports, "__esModule", { value: true });
46
51
  exports.restoreOrchestrator = exports.RestoreOrchestrator = void 0;
52
+ exports.createRestoreOrchestrator = createRestoreOrchestrator;
47
53
  const fs = __importStar(require("fs"));
48
54
  const path = __importStar(require("path"));
49
55
  const os = __importStar(require("os"));
50
56
  const LocalSessionStore_js_1 = require("../cache/LocalSessionStore.js");
51
57
  const types_js_1 = require("../cache/types.js");
52
58
  const stream_tailer_js_1 = require("../capture/stream-tailer.js");
59
+ const paths_js_1 = require("../utils/paths.js");
53
60
  // API configuration
54
61
  const MEMORY_API_URL = process.env.EKKOS_API_URL || 'https://api.ekkos.dev';
55
62
  const CONFIG_PATH = path.join(os.homedir(), '.ekkos', 'config.json');
56
- const STREAM_CACHE_DIR = path.join(os.homedir(), '.ekkos', 'cache', 'sessions');
57
63
  /**
58
64
  * Load auth token from config
59
65
  */
@@ -70,19 +76,31 @@ function loadAuthToken() {
70
76
  return '';
71
77
  }
72
78
  /**
73
- * RestoreOrchestrator - Tiered restore for /continue
79
+ * RestoreOrchestrator - Tiered restore for /continue (Instance-Aware)
74
80
  */
75
81
  class RestoreOrchestrator {
76
- constructor() {
77
- this.localStore = new LocalSessionStore_js_1.LocalSessionStore();
82
+ constructor(instanceId) {
83
+ this.instanceId = (0, paths_js_1.normalizeInstanceId)(instanceId || process.env.EKKOS_INSTANCE_ID);
84
+ this.localStore = (0, LocalSessionStore_js_1.createLocalSessionStore)(this.instanceId);
78
85
  this.authToken = loadAuthToken();
79
86
  }
87
+ /**
88
+ * Get the current instance ID
89
+ */
90
+ getInstanceId() {
91
+ return this.instanceId;
92
+ }
80
93
  /**
81
94
  * Main restore function - attempts tiers in order
82
95
  */
83
96
  async restore(options = {}) {
84
97
  const startTime = Date.now();
85
98
  const lastN = options.last_n || 10;
99
+ // Use provided instanceId or fall back to constructor instanceId
100
+ const instanceId = options.instance_id || this.instanceId;
101
+ if (instanceId !== this.instanceId) {
102
+ this.localStore.setInstanceId(instanceId);
103
+ }
86
104
  // Resolve session
87
105
  let sessionId = options.session_id;
88
106
  let sessionName = options.session_name;
@@ -105,7 +123,7 @@ class RestoreOrchestrator {
105
123
  };
106
124
  }
107
125
  // Try Tier -1: Stream log (has mid-turn content, most recent data)
108
- const streamResult = await this.restoreFromStreamLog(sessionId, sessionName || '', lastN);
126
+ const streamResult = await this.restoreFromStreamLog(instanceId, sessionId, sessionName || '', lastN);
109
127
  if (streamResult.success && streamResult.data) {
110
128
  return {
111
129
  ...streamResult,
@@ -113,7 +131,7 @@ class RestoreOrchestrator {
113
131
  };
114
132
  }
115
133
  // Try Tier 0: Local cache
116
- const localResult = await this.restoreFromLocal(sessionId, sessionName || '', lastN);
134
+ const localResult = await this.restoreFromLocal(instanceId, sessionId, sessionName || '', lastN);
117
135
  if (localResult.success && localResult.data) {
118
136
  return {
119
137
  ...localResult,
@@ -121,7 +139,7 @@ class RestoreOrchestrator {
121
139
  };
122
140
  }
123
141
  // Try Tier 1: Redis
124
- const redisResult = await this.restoreFromRedis(sessionName || sessionId, lastN);
142
+ const redisResult = await this.restoreFromRedis(sessionName || sessionId, lastN, instanceId);
125
143
  if (redisResult.success && redisResult.data) {
126
144
  return {
127
145
  ...redisResult,
@@ -129,7 +147,7 @@ class RestoreOrchestrator {
129
147
  };
130
148
  }
131
149
  // Try Tier 2: Supabase
132
- const supabaseResult = await this.restoreFromSupabase(sessionId, lastN);
150
+ const supabaseResult = await this.restoreFromSupabase(sessionId, lastN, instanceId);
133
151
  if (supabaseResult.success && supabaseResult.data) {
134
152
  return {
135
153
  ...supabaseResult,
@@ -147,12 +165,14 @@ class RestoreOrchestrator {
147
165
  * Tier -1: Restore from stream log (has mid-turn content)
148
166
  * This is checked FIRST because stream logs have the most recent data,
149
167
  * including in-progress turns that haven't been sealed yet.
168
+ *
169
+ * Checks instance-scoped path first, then legacy path as fallback.
150
170
  */
151
- async restoreFromStreamLog(sessionId, sessionName, lastN) {
171
+ async restoreFromStreamLog(instanceId, sessionId, sessionName, lastN) {
152
172
  const startTime = Date.now();
153
- // Find stream log file
154
- const streamLogPath = path.join(STREAM_CACHE_DIR, `${sessionId}.stream.jsonl`);
155
- if (!fs.existsSync(streamLogPath)) {
173
+ // Find stream log file (instance-scoped first, then legacy)
174
+ const streamLogFile = (0, paths_js_1.findStreamLogFile)(instanceId, sessionId);
175
+ if (!streamLogFile) {
156
176
  return {
157
177
  success: false,
158
178
  error: 'No stream log found',
@@ -161,7 +181,7 @@ class RestoreOrchestrator {
161
181
  };
162
182
  }
163
183
  try {
164
- const { turns: streamTurns, latestTurnId } = await (0, stream_tailer_js_1.reconstructTurnFromEvents)(streamLogPath);
184
+ const { turns: streamTurns, latestTurnId } = await (0, stream_tailer_js_1.reconstructTurnFromEvents)(streamLogFile.path);
165
185
  if (streamTurns.size === 0) {
166
186
  return {
167
187
  success: false,
@@ -193,6 +213,9 @@ class RestoreOrchestrator {
193
213
  tools_used: turn.tools.filter(t => t.kind === 'tool_use').map(t => t.name || 'unknown'),
194
214
  files_referenced: [],
195
215
  is_complete: turn.status === 'complete',
216
+ instance_id: streamLogFile.isLegacy ? paths_js_1.DEFAULT_INSTANCE_ID : instanceId,
217
+ session_id: sessionId,
218
+ session_name: sessionName,
196
219
  });
197
220
  }
198
221
  // Sort by turn_id
@@ -213,6 +236,7 @@ class RestoreOrchestrator {
213
236
  const payload = {
214
237
  session_id: sessionId,
215
238
  session_name: sessionName || 'unknown',
239
+ instance_id: streamLogFile.isLegacy ? paths_js_1.DEFAULT_INSTANCE_ID : instanceId,
216
240
  source: 'stream',
217
241
  restored_turns: restoredTurns,
218
242
  latest: {
@@ -257,7 +281,7 @@ class RestoreOrchestrator {
257
281
  /**
258
282
  * Tier 0: Restore from local JSONL cache
259
283
  */
260
- async restoreFromLocal(sessionId, sessionName, lastN) {
284
+ async restoreFromLocal(instanceId, sessionId, sessionName, lastN) {
261
285
  const startTime = Date.now();
262
286
  const turnsResult = this.localStore.getLastTurns(sessionId, lastN);
263
287
  if (!turnsResult.success || !turnsResult.data || turnsResult.data.length === 0) {
@@ -272,6 +296,9 @@ class RestoreOrchestrator {
272
296
  const allTurns = turnsResult.data.map((t) => ({
273
297
  ...t,
274
298
  is_complete: (0, types_js_1.isValidAssistantResponse)(t.assistant_response),
299
+ instance_id: t.instance_id || instanceId,
300
+ session_id: t.session_id || sessionId,
301
+ session_name: t.session_name || sessionName,
275
302
  }));
276
303
  // Separate complete turns from incomplete (pending) turns
277
304
  const completeTurns = allTurns.filter((t) => t.is_complete);
@@ -304,6 +331,7 @@ class RestoreOrchestrator {
304
331
  const payload = {
305
332
  session_id: sessionId,
306
333
  session_name: sessionName || this.localStore.getSessionName(sessionId) || 'unknown',
334
+ instance_id: meta?.instance_id || instanceId,
307
335
  source: 'local',
308
336
  restored_turns: turns,
309
337
  latest: {
@@ -337,7 +365,7 @@ class RestoreOrchestrator {
337
365
  /**
338
366
  * Tier 1: Restore from Redis via API
339
367
  */
340
- async restoreFromRedis(sessionName, lastN) {
368
+ async restoreFromRedis(sessionName, lastN, instanceId) {
341
369
  const startTime = Date.now();
342
370
  if (!this.authToken) {
343
371
  return {
@@ -375,6 +403,9 @@ class RestoreOrchestrator {
375
403
  tools_used: t.agent?.tools_used || [],
376
404
  files_referenced: t.user?.files_referenced || [],
377
405
  is_complete: (0, types_js_1.isValidAssistantResponse)(t.agent?.response),
406
+ instance_id: t.instance_id || instanceId,
407
+ session_id: data.session_id || sessionName,
408
+ session_name: data.session_name || sessionName,
378
409
  }));
379
410
  // Separate complete turns from pending
380
411
  const completeTurns = allTurns.filter((t) => t.is_complete);
@@ -395,6 +426,7 @@ class RestoreOrchestrator {
395
426
  const payload = {
396
427
  session_id: data.session_id || sessionName,
397
428
  session_name: data.session_name || sessionName,
429
+ instance_id: data.instance_id || instanceId,
398
430
  source: 'redis',
399
431
  restored_turns: turns,
400
432
  latest: {
@@ -444,7 +476,7 @@ class RestoreOrchestrator {
444
476
  /**
445
477
  * Tier 2: Restore from Supabase via API
446
478
  */
447
- async restoreFromSupabase(sessionId, lastN) {
479
+ async restoreFromSupabase(sessionId, lastN, instanceId) {
448
480
  const startTime = Date.now();
449
481
  if (!this.authToken) {
450
482
  return {
@@ -483,6 +515,9 @@ class RestoreOrchestrator {
483
515
  tools_used: [],
484
516
  files_referenced: c.metadata?.file_changes?.map((f) => f.path) || [],
485
517
  is_complete: (0, types_js_1.isValidAssistantResponse)(c.assistant_response),
518
+ instance_id: c.metadata?.instance_id || instanceId,
519
+ session_id: sessionId,
520
+ session_name: data.session_name || 'unknown',
486
521
  }));
487
522
  // Separate complete turns from pending
488
523
  const completeTurns = allTurns.filter((t) => t.is_complete);
@@ -503,6 +538,7 @@ class RestoreOrchestrator {
503
538
  const payload = {
504
539
  session_id: sessionId,
505
540
  session_name: data.session_name || 'unknown',
541
+ instance_id: instanceId,
506
542
  source: 'supabase',
507
543
  restored_turns: turns,
508
544
  latest: {
@@ -560,6 +596,7 @@ class RestoreOrchestrator {
560
596
  '<system-reminder>',
561
597
  'CONTEXT RESTORED (ekkOS /continue)',
562
598
  `Session: ${payload.session_name} (${payload.session_id})`,
599
+ `Instance: ${payload.instance_id || paths_js_1.DEFAULT_INSTANCE_ID}`,
563
600
  `Source: ${payload.source}${isStreamRestore ? ' (real-time stream)' : ''}`,
564
601
  isInProgress ? `Status: IN_PROGRESS (mid-turn restore)` : `Turns restored: ${payload.restored_turns.length}`,
565
602
  '',
@@ -570,7 +607,7 @@ class RestoreOrchestrator {
570
607
  if (openLoops && openLoops.length > 0) {
571
608
  lines.push('## Open Loops (machine-derived)');
572
609
  for (const loop of openLoops) {
573
- lines.push(`- ⚠️ ${loop.detail}`);
610
+ lines.push(`- Warning: ${loop.detail}`);
574
611
  }
575
612
  lines.push('');
576
613
  }
@@ -585,7 +622,7 @@ class RestoreOrchestrator {
585
622
  // Special instruction for mid-turn resume
586
623
  lines.push('INSTRUCTION: Continue from the exact point shown above.');
587
624
  lines.push('Do not recap. Do not restart. Continue the sentence/action if mid-sentence.');
588
- lines.push('Start your response with: "Continuing -"');
625
+ lines.push('Start your response with: "Continuing -"');
589
626
  }
590
627
  else {
591
628
  // Standard restore format
@@ -598,7 +635,7 @@ class RestoreOrchestrator {
598
635
  lines.push('\n[...truncated...]');
599
636
  }
600
637
  lines.push('');
601
- lines.push('## Recent Turns (older newer)');
638
+ lines.push('## Recent Turns (older to newer)');
602
639
  // Add turn summaries (skip last one since it's shown in detail above)
603
640
  const turnsToShow = payload.restored_turns.slice(0, -1);
604
641
  for (let i = 0; i < turnsToShow.length; i++) {
@@ -611,7 +648,7 @@ class RestoreOrchestrator {
611
648
  lines.push('');
612
649
  lines.push('INSTRUCTION: Resume seamlessly where you left off.');
613
650
  lines.push('Do not ask "what were we doing?"');
614
- lines.push('Start your response with: "Continuing -"');
651
+ lines.push('Start your response with: "Continuing -"');
615
652
  }
616
653
  lines.push('</system-reminder>');
617
654
  return lines.join('\n');
@@ -625,9 +662,14 @@ class RestoreOrchestrator {
625
662
  local_sessions: stats.session_count,
626
663
  local_turns: stats.total_turns,
627
664
  local_size_bytes: stats.cache_size_bytes,
665
+ instance_id: stats.instance_id,
628
666
  };
629
667
  }
630
668
  }
631
669
  exports.RestoreOrchestrator = RestoreOrchestrator;
632
- // Export singleton instance
670
+ // Export factory function
671
+ function createRestoreOrchestrator(instanceId) {
672
+ return new RestoreOrchestrator(instanceId);
673
+ }
674
+ // Export default instance using EKKOS_INSTANCE_ID from env
633
675
  exports.restoreOrchestrator = new RestoreOrchestrator();
@@ -0,0 +1,125 @@
1
+ /**
2
+ * ekkOS Path Utilities - Instance-Aware Path Resolution
3
+ *
4
+ * Per ekkOS Onboarding Spec v1.2 FINAL + ADDENDUM:
5
+ * - All Tier 0 cache paths MUST be: ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.jsonl
6
+ * - All persisted records MUST include: instanceId, sessionId, sessionName
7
+ *
8
+ * This module provides canonical path helpers for the entire CLI.
9
+ */
10
+ export declare const DEFAULT_INSTANCE_ID = "default";
11
+ /**
12
+ * Normalize instanceId - use 'default' if empty/undefined
13
+ */
14
+ export declare function normalizeInstanceId(instanceId?: string | null): string;
15
+ /**
16
+ * Get the base ekkOS config directory
17
+ * ~/.ekkos on Unix, %USERPROFILE%\.ekkos on Windows
18
+ */
19
+ export declare function getConfigDir(): string;
20
+ /**
21
+ * Get the cache directory
22
+ * ~/.ekkos/cache
23
+ */
24
+ export declare function getCacheDir(): string;
25
+ /**
26
+ * Get the sessions cache directory
27
+ * ~/.ekkos/cache/sessions
28
+ */
29
+ export declare function getSessionsDir(): string;
30
+ /**
31
+ * Get the instance-scoped sessions directory
32
+ * ~/.ekkos/cache/sessions/{instanceId}
33
+ */
34
+ export declare function getInstanceSessionsDir(instanceId?: string | null): string;
35
+ /**
36
+ * Get the path to a session's turns JSONL file (instance-scoped)
37
+ * ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.jsonl
38
+ */
39
+ export declare function getTurnsPath(instanceId: string | null | undefined, sessionId: string): string;
40
+ /**
41
+ * Get the path to a session's metadata file (instance-scoped)
42
+ * ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.meta.json
43
+ */
44
+ export declare function getMetaPath(instanceId: string | null | undefined, sessionId: string): string;
45
+ /**
46
+ * Get the path to a session's stream log file (instance-scoped)
47
+ * ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.stream.jsonl
48
+ */
49
+ export declare function getStreamLogPath(instanceId: string | null | undefined, sessionId: string): string;
50
+ /**
51
+ * Get the path to a session's state file (instance-scoped)
52
+ * ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.state.json
53
+ */
54
+ export declare function getStreamStatePath(instanceId: string | null | undefined, sessionId: string): string;
55
+ /**
56
+ * Get the path to the session index file (instance-scoped)
57
+ * ~/.ekkos/cache/sessions/{instanceId}/index.json
58
+ */
59
+ export declare function getIndexPath(instanceId?: string | null): string;
60
+ /**
61
+ * Get the instances directory
62
+ * ~/.ekkos/instances
63
+ */
64
+ export declare function getInstancesDir(): string;
65
+ /**
66
+ * Get the path to an instance file
67
+ * ~/.ekkos/instances/{instanceId}.json
68
+ */
69
+ export declare function getInstanceFilePath(instanceId: string): string;
70
+ /**
71
+ * Legacy path for backward compatibility (read-only fallback)
72
+ * ~/.ekkos/cache/sessions/{sessionId}.jsonl
73
+ */
74
+ export declare function getLegacyTurnsPath(sessionId: string): string;
75
+ /**
76
+ * Legacy metadata path for backward compatibility (read-only fallback)
77
+ * ~/.ekkos/cache/sessions/{sessionId}.meta.json
78
+ */
79
+ export declare function getLegacyMetaPath(sessionId: string): string;
80
+ /**
81
+ * Legacy stream log path for backward compatibility (read-only fallback)
82
+ * ~/.ekkos/cache/sessions/{sessionId}.stream.jsonl
83
+ */
84
+ export declare function getLegacyStreamLogPath(sessionId: string): string;
85
+ /**
86
+ * Ensure a directory exists (mkdirp)
87
+ */
88
+ export declare function ensureDir(dirPath: string): void;
89
+ /**
90
+ * Ensure the instance sessions directory exists
91
+ */
92
+ export declare function ensureInstanceDir(instanceId?: string | null): string;
93
+ /**
94
+ * Ensure the base ekkOS directories exist
95
+ */
96
+ export declare function ensureBaseDirs(): void;
97
+ /**
98
+ * List all instance directories under sessions cache
99
+ * Returns array of instanceIds
100
+ */
101
+ export declare function listInstanceIds(): string[];
102
+ /**
103
+ * Check if a file exists at the instance-scoped path
104
+ * If not, check legacy path and return which one exists (if any)
105
+ */
106
+ export declare function findTurnsFile(instanceId: string | null | undefined, sessionId: string): {
107
+ path: string;
108
+ isLegacy: boolean;
109
+ } | null;
110
+ /**
111
+ * Check if a meta file exists at the instance-scoped path
112
+ * If not, check legacy path and return which one exists (if any)
113
+ */
114
+ export declare function findMetaFile(instanceId: string | null | undefined, sessionId: string): {
115
+ path: string;
116
+ isLegacy: boolean;
117
+ } | null;
118
+ /**
119
+ * Check if a stream log exists at the instance-scoped path
120
+ * If not, check legacy path and return which one exists (if any)
121
+ */
122
+ export declare function findStreamLogFile(instanceId: string | null | undefined, sessionId: string): {
123
+ path: string;
124
+ isLegacy: boolean;
125
+ } | null;
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ /**
3
+ * ekkOS Path Utilities - Instance-Aware Path Resolution
4
+ *
5
+ * Per ekkOS Onboarding Spec v1.2 FINAL + ADDENDUM:
6
+ * - All Tier 0 cache paths MUST be: ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.jsonl
7
+ * - All persisted records MUST include: instanceId, sessionId, sessionName
8
+ *
9
+ * This module provides canonical path helpers for the entire CLI.
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.DEFAULT_INSTANCE_ID = void 0;
46
+ exports.normalizeInstanceId = normalizeInstanceId;
47
+ exports.getConfigDir = getConfigDir;
48
+ exports.getCacheDir = getCacheDir;
49
+ exports.getSessionsDir = getSessionsDir;
50
+ exports.getInstanceSessionsDir = getInstanceSessionsDir;
51
+ exports.getTurnsPath = getTurnsPath;
52
+ exports.getMetaPath = getMetaPath;
53
+ exports.getStreamLogPath = getStreamLogPath;
54
+ exports.getStreamStatePath = getStreamStatePath;
55
+ exports.getIndexPath = getIndexPath;
56
+ exports.getInstancesDir = getInstancesDir;
57
+ exports.getInstanceFilePath = getInstanceFilePath;
58
+ exports.getLegacyTurnsPath = getLegacyTurnsPath;
59
+ exports.getLegacyMetaPath = getLegacyMetaPath;
60
+ exports.getLegacyStreamLogPath = getLegacyStreamLogPath;
61
+ exports.ensureDir = ensureDir;
62
+ exports.ensureInstanceDir = ensureInstanceDir;
63
+ exports.ensureBaseDirs = ensureBaseDirs;
64
+ exports.listInstanceIds = listInstanceIds;
65
+ exports.findTurnsFile = findTurnsFile;
66
+ exports.findMetaFile = findMetaFile;
67
+ exports.findStreamLogFile = findStreamLogFile;
68
+ const path = __importStar(require("path"));
69
+ const os = __importStar(require("os"));
70
+ const fs = __importStar(require("fs"));
71
+ // Base directories
72
+ const HOME = os.homedir();
73
+ const EKKOS_DIR = path.join(HOME, '.ekkos');
74
+ const CACHE_DIR = path.join(EKKOS_DIR, 'cache');
75
+ const SESSIONS_DIR = path.join(CACHE_DIR, 'sessions');
76
+ const INSTANCES_DIR = path.join(EKKOS_DIR, 'instances');
77
+ // Default instanceId when none provided (backward compatibility)
78
+ exports.DEFAULT_INSTANCE_ID = 'default';
79
+ /**
80
+ * Normalize instanceId - use 'default' if empty/undefined
81
+ */
82
+ function normalizeInstanceId(instanceId) {
83
+ if (!instanceId || instanceId.trim() === '') {
84
+ return exports.DEFAULT_INSTANCE_ID;
85
+ }
86
+ return instanceId.trim();
87
+ }
88
+ /**
89
+ * Get the base ekkOS config directory
90
+ * ~/.ekkos on Unix, %USERPROFILE%\.ekkos on Windows
91
+ */
92
+ function getConfigDir() {
93
+ return EKKOS_DIR;
94
+ }
95
+ /**
96
+ * Get the cache directory
97
+ * ~/.ekkos/cache
98
+ */
99
+ function getCacheDir() {
100
+ return CACHE_DIR;
101
+ }
102
+ /**
103
+ * Get the sessions cache directory
104
+ * ~/.ekkos/cache/sessions
105
+ */
106
+ function getSessionsDir() {
107
+ return SESSIONS_DIR;
108
+ }
109
+ /**
110
+ * Get the instance-scoped sessions directory
111
+ * ~/.ekkos/cache/sessions/{instanceId}
112
+ */
113
+ function getInstanceSessionsDir(instanceId) {
114
+ const normalizedId = normalizeInstanceId(instanceId);
115
+ return path.join(SESSIONS_DIR, normalizedId);
116
+ }
117
+ /**
118
+ * Get the path to a session's turns JSONL file (instance-scoped)
119
+ * ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.jsonl
120
+ */
121
+ function getTurnsPath(instanceId, sessionId) {
122
+ const instanceDir = getInstanceSessionsDir(instanceId);
123
+ return path.join(instanceDir, `${sessionId}.jsonl`);
124
+ }
125
+ /**
126
+ * Get the path to a session's metadata file (instance-scoped)
127
+ * ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.meta.json
128
+ */
129
+ function getMetaPath(instanceId, sessionId) {
130
+ const instanceDir = getInstanceSessionsDir(instanceId);
131
+ return path.join(instanceDir, `${sessionId}.meta.json`);
132
+ }
133
+ /**
134
+ * Get the path to a session's stream log file (instance-scoped)
135
+ * ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.stream.jsonl
136
+ */
137
+ function getStreamLogPath(instanceId, sessionId) {
138
+ const instanceDir = getInstanceSessionsDir(instanceId);
139
+ return path.join(instanceDir, `${sessionId}.stream.jsonl`);
140
+ }
141
+ /**
142
+ * Get the path to a session's state file (instance-scoped)
143
+ * ~/.ekkos/cache/sessions/{instanceId}/{sessionId}.state.json
144
+ */
145
+ function getStreamStatePath(instanceId, sessionId) {
146
+ const instanceDir = getInstanceSessionsDir(instanceId);
147
+ return path.join(instanceDir, `${sessionId}.state.json`);
148
+ }
149
+ /**
150
+ * Get the path to the session index file (instance-scoped)
151
+ * ~/.ekkos/cache/sessions/{instanceId}/index.json
152
+ */
153
+ function getIndexPath(instanceId) {
154
+ const instanceDir = getInstanceSessionsDir(instanceId);
155
+ return path.join(instanceDir, 'index.json');
156
+ }
157
+ /**
158
+ * Get the instances directory
159
+ * ~/.ekkos/instances
160
+ */
161
+ function getInstancesDir() {
162
+ return INSTANCES_DIR;
163
+ }
164
+ /**
165
+ * Get the path to an instance file
166
+ * ~/.ekkos/instances/{instanceId}.json
167
+ */
168
+ function getInstanceFilePath(instanceId) {
169
+ return path.join(INSTANCES_DIR, `${instanceId}.json`);
170
+ }
171
+ /**
172
+ * Legacy path for backward compatibility (read-only fallback)
173
+ * ~/.ekkos/cache/sessions/{sessionId}.jsonl
174
+ */
175
+ function getLegacyTurnsPath(sessionId) {
176
+ return path.join(SESSIONS_DIR, `${sessionId}.jsonl`);
177
+ }
178
+ /**
179
+ * Legacy metadata path for backward compatibility (read-only fallback)
180
+ * ~/.ekkos/cache/sessions/{sessionId}.meta.json
181
+ */
182
+ function getLegacyMetaPath(sessionId) {
183
+ return path.join(SESSIONS_DIR, `${sessionId}.meta.json`);
184
+ }
185
+ /**
186
+ * Legacy stream log path for backward compatibility (read-only fallback)
187
+ * ~/.ekkos/cache/sessions/{sessionId}.stream.jsonl
188
+ */
189
+ function getLegacyStreamLogPath(sessionId) {
190
+ return path.join(SESSIONS_DIR, `${sessionId}.stream.jsonl`);
191
+ }
192
+ /**
193
+ * Ensure a directory exists (mkdirp)
194
+ */
195
+ function ensureDir(dirPath) {
196
+ if (!fs.existsSync(dirPath)) {
197
+ fs.mkdirSync(dirPath, { recursive: true });
198
+ }
199
+ }
200
+ /**
201
+ * Ensure the instance sessions directory exists
202
+ */
203
+ function ensureInstanceDir(instanceId) {
204
+ const instanceDir = getInstanceSessionsDir(instanceId);
205
+ ensureDir(instanceDir);
206
+ return instanceDir;
207
+ }
208
+ /**
209
+ * Ensure the base ekkOS directories exist
210
+ */
211
+ function ensureBaseDirs() {
212
+ ensureDir(EKKOS_DIR);
213
+ ensureDir(CACHE_DIR);
214
+ ensureDir(SESSIONS_DIR);
215
+ ensureDir(INSTANCES_DIR);
216
+ }
217
+ /**
218
+ * List all instance directories under sessions cache
219
+ * Returns array of instanceIds
220
+ */
221
+ function listInstanceIds() {
222
+ ensureDir(SESSIONS_DIR);
223
+ try {
224
+ const entries = fs.readdirSync(SESSIONS_DIR, { withFileTypes: true });
225
+ return entries
226
+ .filter(e => e.isDirectory())
227
+ .map(e => e.name);
228
+ }
229
+ catch {
230
+ return [];
231
+ }
232
+ }
233
+ /**
234
+ * Check if a file exists at the instance-scoped path
235
+ * If not, check legacy path and return which one exists (if any)
236
+ */
237
+ function findTurnsFile(instanceId, sessionId) {
238
+ // Try instance-scoped path first
239
+ const instancePath = getTurnsPath(instanceId, sessionId);
240
+ if (fs.existsSync(instancePath)) {
241
+ return { path: instancePath, isLegacy: false };
242
+ }
243
+ // Try legacy path as fallback (read-only)
244
+ const legacyPath = getLegacyTurnsPath(sessionId);
245
+ if (fs.existsSync(legacyPath)) {
246
+ return { path: legacyPath, isLegacy: true };
247
+ }
248
+ return null;
249
+ }
250
+ /**
251
+ * Check if a meta file exists at the instance-scoped path
252
+ * If not, check legacy path and return which one exists (if any)
253
+ */
254
+ function findMetaFile(instanceId, sessionId) {
255
+ // Try instance-scoped path first
256
+ const instancePath = getMetaPath(instanceId, sessionId);
257
+ if (fs.existsSync(instancePath)) {
258
+ return { path: instancePath, isLegacy: false };
259
+ }
260
+ // Try legacy path as fallback (read-only)
261
+ const legacyPath = getLegacyMetaPath(sessionId);
262
+ if (fs.existsSync(legacyPath)) {
263
+ return { path: legacyPath, isLegacy: true };
264
+ }
265
+ return null;
266
+ }
267
+ /**
268
+ * Check if a stream log exists at the instance-scoped path
269
+ * If not, check legacy path and return which one exists (if any)
270
+ */
271
+ function findStreamLogFile(instanceId, sessionId) {
272
+ // Try instance-scoped path first
273
+ const instancePath = getStreamLogPath(instanceId, sessionId);
274
+ if (fs.existsSync(instancePath)) {
275
+ return { path: instancePath, isLegacy: false };
276
+ }
277
+ // Try legacy path as fallback (read-only)
278
+ const legacyPath = getLegacyStreamLogPath(sessionId);
279
+ if (fs.existsSync(legacyPath)) {
280
+ return { path: legacyPath, isLegacy: true };
281
+ }
282
+ return null;
283
+ }