@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.
- package/dist/agent/daemon.d.ts +86 -0
- package/dist/agent/daemon.js +297 -0
- package/dist/agent/pty-runner.d.ts +51 -0
- package/dist/agent/pty-runner.js +184 -0
- package/dist/cache/LocalSessionStore.d.ts +34 -21
- package/dist/cache/LocalSessionStore.js +169 -53
- package/dist/cache/capture.d.ts +19 -11
- package/dist/cache/capture.js +243 -76
- package/dist/cache/types.d.ts +14 -1
- package/dist/commands/agent.d.ts +44 -0
- package/dist/commands/agent.js +300 -0
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/doctor.js +175 -87
- package/dist/commands/hooks.d.ts +109 -0
- package/dist/commands/hooks.js +668 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +357 -85
- package/dist/commands/setup-remote.d.ts +20 -0
- package/dist/commands/setup-remote.js +467 -0
- package/dist/index.js +116 -1
- package/dist/restore/RestoreOrchestrator.d.ts +17 -3
- package/dist/restore/RestoreOrchestrator.js +64 -22
- package/dist/utils/paths.d.ts +125 -0
- package/dist/utils/paths.js +283 -0
- package/dist/utils/state.d.ts +2 -0
- package/package.json +1 -1
- package/templates/ekkos-manifest.json +223 -0
- package/templates/helpers/json-parse.cjs +101 -0
- package/templates/hooks/assistant-response.ps1 +256 -0
- package/templates/hooks/assistant-response.sh +124 -64
- package/templates/hooks/session-start.ps1 +107 -2
- package/templates/hooks/session-start.sh +201 -166
- package/templates/hooks/stop.ps1 +124 -3
- package/templates/hooks/stop.sh +470 -843
- package/templates/hooks/user-prompt-submit.ps1 +107 -22
- package/templates/hooks/user-prompt-submit.sh +403 -393
- package/templates/project-stubs/session-start.ps1 +63 -0
- package/templates/project-stubs/session-start.sh +55 -0
- package/templates/project-stubs/stop.ps1 +63 -0
- package/templates/project-stubs/stop.sh +55 -0
- package/templates/project-stubs/user-prompt-submit.ps1 +63 -0
- package/templates/project-stubs/user-prompt-submit.sh +55 -0
- package/templates/shared/hooks-enabled.json +22 -0
- 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.
|
|
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
|
|
155
|
-
if (!
|
|
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)(
|
|
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(`-
|
|
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: "
|
|
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
|
|
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: "
|
|
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
|
|
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
|
+
}
|