@ekkos/cli 1.3.8 → 1.3.9
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/commands/gemini.js +11 -10
- package/dist/commands/run.d.ts +5 -0
- package/dist/commands/run.js +430 -21
- package/dist/index.js +81 -36
- package/dist/utils/proxy-url.d.ts +6 -1
- package/dist/utils/proxy-url.js +16 -2
- package/package.json +22 -11
- package/templates/agents/prune.md +83 -0
- package/templates/agents/rewind.md +84 -0
- package/templates/agents/scout.md +102 -0
- package/templates/agents/trace.md +99 -0
- package/templates/commands/continue.md +47 -0
package/dist/commands/gemini.js
CHANGED
|
@@ -94,9 +94,18 @@ async function showPulseLoadedBanner() {
|
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
96
|
* Resolve Gemini CLI binary path.
|
|
97
|
-
* Checks
|
|
97
|
+
* Checks PATH first (which/where), then falls back to common locations.
|
|
98
98
|
*/
|
|
99
99
|
function resolveGeminiPath() {
|
|
100
|
+
// 1. PATH lookup first to respect nvm/volta/npm-global settings
|
|
101
|
+
const whichCmd = isWindows ? 'where gemini' : 'which gemini';
|
|
102
|
+
try {
|
|
103
|
+
const result = (0, child_process_1.execSync)(whichCmd, { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
104
|
+
if (result)
|
|
105
|
+
return result.split('\n')[0];
|
|
106
|
+
}
|
|
107
|
+
catch { /* not found in PATH */ }
|
|
108
|
+
// 2. Fallback to common global install locations
|
|
100
109
|
if (!isWindows) {
|
|
101
110
|
const pathsToCheck = [
|
|
102
111
|
'/opt/homebrew/bin/gemini',
|
|
@@ -109,15 +118,7 @@ function resolveGeminiPath() {
|
|
|
109
118
|
return p;
|
|
110
119
|
}
|
|
111
120
|
}
|
|
112
|
-
//
|
|
113
|
-
const whichCmd = isWindows ? 'where gemini' : 'which gemini';
|
|
114
|
-
try {
|
|
115
|
-
const result = (0, child_process_1.execSync)(whichCmd, { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
116
|
-
if (result)
|
|
117
|
-
return result.split('\n')[0];
|
|
118
|
-
}
|
|
119
|
-
catch { /* not found */ }
|
|
120
|
-
// Fallback — let spawn resolve it (will error with helpful message)
|
|
121
|
+
// 3. Ultimate fallback — let spawn resolve it (will error with helpful message)
|
|
121
122
|
return 'gemini';
|
|
122
123
|
}
|
|
123
124
|
/**
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -2,12 +2,17 @@ interface RunOptions {
|
|
|
2
2
|
session?: string;
|
|
3
3
|
verbose?: boolean;
|
|
4
4
|
bypass?: boolean;
|
|
5
|
+
pulse?: boolean;
|
|
5
6
|
doctor?: boolean;
|
|
6
7
|
noInject?: boolean;
|
|
7
8
|
research?: boolean;
|
|
8
9
|
noProxy?: boolean;
|
|
9
10
|
dashboard?: boolean;
|
|
10
11
|
addDirs?: string[];
|
|
12
|
+
model?: string | boolean;
|
|
13
|
+
contextWindow?: string;
|
|
14
|
+
continueLast?: boolean;
|
|
15
|
+
resumeSession?: string;
|
|
11
16
|
slashOpenDelayMs?: number;
|
|
12
17
|
charDelayMs?: number;
|
|
13
18
|
postEnterDelayMs?: number;
|
package/dist/commands/run.js
CHANGED
|
@@ -86,6 +86,244 @@ function getConfig(options) {
|
|
|
86
86
|
};
|
|
87
87
|
/* eslint-enable no-restricted-syntax */
|
|
88
88
|
}
|
|
89
|
+
const MAX_OUTPUT_1M_MODELS = '128000';
|
|
90
|
+
const MAX_OUTPUT_200K_OPUS_SONNET = '32768';
|
|
91
|
+
let runtimeClaudeCodeVersion = null;
|
|
92
|
+
let runtimeClaudeContextWindow = 'auto';
|
|
93
|
+
let runtimeClaudeLaunchModel;
|
|
94
|
+
function normalizeContextWindowOption(value) {
|
|
95
|
+
const normalized = (value || '').trim().toLowerCase();
|
|
96
|
+
if (normalized === '200k' || normalized === '200000')
|
|
97
|
+
return '200k';
|
|
98
|
+
if (normalized === '1m' || normalized === '1000000')
|
|
99
|
+
return '1m';
|
|
100
|
+
return 'auto';
|
|
101
|
+
}
|
|
102
|
+
function normalizeRequestedLaunchModel(value) {
|
|
103
|
+
if (typeof value !== 'string')
|
|
104
|
+
return {};
|
|
105
|
+
const raw = (value || '').trim();
|
|
106
|
+
if (!raw)
|
|
107
|
+
return {};
|
|
108
|
+
if (raw.toLowerCase() === 'default') {
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
const oneMillionMatch = raw.match(/^(.*)\[1m\]$/i);
|
|
112
|
+
if (oneMillionMatch) {
|
|
113
|
+
return {
|
|
114
|
+
model: oneMillionMatch[1].trim(),
|
|
115
|
+
syntheticContextWindow: '1m',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const lower = raw.toLowerCase();
|
|
119
|
+
if (lower.endsWith('-200k')) {
|
|
120
|
+
return {
|
|
121
|
+
model: raw.slice(0, -5).trim(),
|
|
122
|
+
syntheticContextWindow: '200k',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (lower.endsWith('-1m')) {
|
|
126
|
+
return {
|
|
127
|
+
model: raw.slice(0, -3).trim(),
|
|
128
|
+
syntheticContextWindow: '1m',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return { model: raw };
|
|
132
|
+
}
|
|
133
|
+
function modelSupportsOneMillionContext(model) {
|
|
134
|
+
const normalized = (model || 'default').trim().toLowerCase();
|
|
135
|
+
if (!normalized || normalized === 'default')
|
|
136
|
+
return true;
|
|
137
|
+
if (normalized === 'opus' || normalized === 'sonnet')
|
|
138
|
+
return true;
|
|
139
|
+
return normalized.includes('claude-opus-4-6') || normalized.includes('claude-sonnet-4-6');
|
|
140
|
+
}
|
|
141
|
+
function buildClaudeLaunchModelArg(model, contextWindow) {
|
|
142
|
+
if (!model)
|
|
143
|
+
return undefined;
|
|
144
|
+
const normalized = normalizeRequestedLaunchModel(model).model;
|
|
145
|
+
if (!normalized)
|
|
146
|
+
return undefined;
|
|
147
|
+
if (contextWindow === '1m' && modelSupportsOneMillionContext(normalized)) {
|
|
148
|
+
return `${normalized}[1m]`;
|
|
149
|
+
}
|
|
150
|
+
return normalized;
|
|
151
|
+
}
|
|
152
|
+
function isTwoHundredKFixedOutputModel(model) {
|
|
153
|
+
const normalized = (model || '').trim().toLowerCase();
|
|
154
|
+
if (!normalized)
|
|
155
|
+
return false;
|
|
156
|
+
if (normalized === 'opus' || normalized === 'sonnet')
|
|
157
|
+
return true;
|
|
158
|
+
return /^claude-(?:opus|sonnet)-4-(?:5|6)(?:$|-)/.test(normalized);
|
|
159
|
+
}
|
|
160
|
+
function resolveClaudeMaxOutputTokens(model, contextWindow) {
|
|
161
|
+
if (process.env.EKKOS_MAX_OUTPUT_TOKENS) {
|
|
162
|
+
return process.env.EKKOS_MAX_OUTPUT_TOKENS;
|
|
163
|
+
}
|
|
164
|
+
if (contextWindow === '200k' && isTwoHundredKFixedOutputModel(model)) {
|
|
165
|
+
return MAX_OUTPUT_200K_OPUS_SONNET;
|
|
166
|
+
}
|
|
167
|
+
return MAX_OUTPUT_1M_MODELS;
|
|
168
|
+
}
|
|
169
|
+
function shouldOpenLaunchSelector(options) {
|
|
170
|
+
return options.model === true;
|
|
171
|
+
}
|
|
172
|
+
function renderClaudeLaunchSelectorIntro() {
|
|
173
|
+
const neonCyan = chalk_1.default.hex('#00f5ff');
|
|
174
|
+
const acidGreen = chalk_1.default.hex('#9dff00');
|
|
175
|
+
const signalAmber = chalk_1.default.hex('#ffb300');
|
|
176
|
+
const steel = chalk_1.default.hex('#8a92a6');
|
|
177
|
+
const width = 68;
|
|
178
|
+
const line = (text) => {
|
|
179
|
+
const pad = Math.max(0, width - stripAnsi(text).length);
|
|
180
|
+
return `${neonCyan('║')} ${text}${' '.repeat(pad)}${neonCyan('║')}`;
|
|
181
|
+
};
|
|
182
|
+
console.log('');
|
|
183
|
+
console.log(neonCyan('╔══════════════════════════════════════════════════════════════════════╗'));
|
|
184
|
+
console.log(line(chalk_1.default.white.bold('ekkOS.dev // PULSE')));
|
|
185
|
+
console.log(neonCyan('╠══════════════════════════════════════════════════════════════════════╣'));
|
|
186
|
+
console.log(line(`${acidGreen('200K')} ${steel('compat lane')} ${chalk_1.default.white('// Opus/Sonnet 4.5/4.6 => 32,768 output')}`));
|
|
187
|
+
console.log(line(`${signalAmber(' 1M')} ${steel('wide lane')} ${chalk_1.default.white('// Opus/Sonnet 4.6 => 128,000 output')}`));
|
|
188
|
+
console.log(line(steel('Pick a launch vector, then a fixed runtime profile. No hidden context prompt.')));
|
|
189
|
+
console.log(neonCyan('╚══════════════════════════════════════════════════════════════════════╝'));
|
|
190
|
+
console.log('');
|
|
191
|
+
}
|
|
192
|
+
function buildLaunchModeChoices() {
|
|
193
|
+
return [
|
|
194
|
+
{
|
|
195
|
+
name: `${chalk_1.default.hex('#00f5ff').bold('Fresh boot')} ${chalk_1.default.gray('// new shell')}`,
|
|
196
|
+
value: 'fresh',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: `${chalk_1.default.hex('#9dff00').bold('Resume recent')} ${chalk_1.default.gray('// latest thread')}`,
|
|
200
|
+
value: 'continue',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: `${chalk_1.default.hex('#ffb300').bold('Resume by ID')} ${chalk_1.default.gray('// targeted reconnect')}`,
|
|
204
|
+
value: 'resume',
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
}
|
|
208
|
+
function buildLaunchModelChoices() {
|
|
209
|
+
const cyan = chalk_1.default.hex('#00f5ff');
|
|
210
|
+
const amber = chalk_1.default.hex('#ffb300');
|
|
211
|
+
const green = chalk_1.default.hex('#00ff88');
|
|
212
|
+
return [
|
|
213
|
+
{
|
|
214
|
+
name: `${cyan.bold('Auto route')} ${chalk_1.default.gray('// Claude decides profile')}`,
|
|
215
|
+
value: 'default',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: `${cyan.bold('Claude Opus 4.5')} ${green('[200K]')} ${chalk_1.default.gray('// out 32,768')}`,
|
|
219
|
+
value: 'claude-opus-4-5',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: `${cyan.bold('Claude Opus 4.6')} ${green('[200K]')} ${chalk_1.default.gray('// out 32,768')}`,
|
|
223
|
+
value: 'claude-opus-4-6-200k',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: `${cyan.bold('Claude Opus 4.6')} ${amber('[1M]')} ${chalk_1.default.gray('// out 128,000')}`,
|
|
227
|
+
value: 'claude-opus-4-6-1m',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: `${cyan.bold('Claude Sonnet 4.5')} ${green('[200K]')} ${chalk_1.default.gray('// out 32,768')}`,
|
|
231
|
+
value: 'claude-sonnet-4-5',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: `${cyan.bold('Claude Sonnet 4.6')} ${green('[200K]')} ${chalk_1.default.gray('// out 32,768')}`,
|
|
235
|
+
value: 'claude-sonnet-4-6-200k',
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: `${cyan.bold('Claude Sonnet 4.6')} ${amber('[1M]')} ${chalk_1.default.gray('// out 128,000')}`,
|
|
239
|
+
value: 'claude-sonnet-4-6-1m',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: `${cyan.bold('Claude Haiku 4.5')} ${green('[200K]')} ${chalk_1.default.gray('// fast lane')}`,
|
|
243
|
+
value: 'claude-haiku-4-5',
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
async function resolveClaudeLaunchSelection(options) {
|
|
248
|
+
const requestedModel = normalizeRequestedLaunchModel(typeof options.model === 'string' ? options.model : process.env.EKKOS_CLAUDE_MODEL);
|
|
249
|
+
let model = requestedModel.model;
|
|
250
|
+
let contextWindow = requestedModel.syntheticContextWindow
|
|
251
|
+
|| normalizeContextWindowOption(options.contextWindow || process.env.EKKOS_CLAUDE_CONTEXT_WINDOW);
|
|
252
|
+
let continueLast = options.continueLast || false;
|
|
253
|
+
let resumeSession = (options.resumeSession || '').trim();
|
|
254
|
+
const shouldShowLaunchWindow = shouldOpenLaunchSelector(options)
|
|
255
|
+
&& process.stdin.isTTY === true
|
|
256
|
+
&& process.stdout.isTTY === true
|
|
257
|
+
&& process.env.EKKOS_DISABLE_LAUNCH_WINDOW !== '1';
|
|
258
|
+
if (!shouldShowLaunchWindow) {
|
|
259
|
+
if (!modelSupportsOneMillionContext(model)) {
|
|
260
|
+
contextWindow = '200k';
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
model,
|
|
264
|
+
contextWindow,
|
|
265
|
+
continueLast: !!continueLast && !resumeSession,
|
|
266
|
+
resumeSession: resumeSession || undefined,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
const { default: inquirer } = await Promise.resolve().then(() => __importStar(require('inquirer')));
|
|
270
|
+
renderClaudeLaunchSelectorIntro();
|
|
271
|
+
const defaultLaunchMode = resumeSession ? 'resume' : continueLast ? 'continue' : 'fresh';
|
|
272
|
+
const firstPrompt = await inquirer.prompt([
|
|
273
|
+
{
|
|
274
|
+
type: 'list',
|
|
275
|
+
name: 'launchMode',
|
|
276
|
+
message: chalk_1.default.hex('#00f5ff').bold('>> Boot vector'),
|
|
277
|
+
default: defaultLaunchMode,
|
|
278
|
+
pageSize: 3,
|
|
279
|
+
loop: false,
|
|
280
|
+
choices: buildLaunchModeChoices(),
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
type: 'list',
|
|
284
|
+
name: 'model',
|
|
285
|
+
message: chalk_1.default.hex('#00f5ff').bold('>> Model lane'),
|
|
286
|
+
default: model || 'default',
|
|
287
|
+
pageSize: 8,
|
|
288
|
+
loop: false,
|
|
289
|
+
choices: buildLaunchModelChoices(),
|
|
290
|
+
},
|
|
291
|
+
]);
|
|
292
|
+
const selectedProfile = normalizeRequestedLaunchModel(firstPrompt.model);
|
|
293
|
+
model = firstPrompt.model === 'default' ? undefined : selectedProfile.model;
|
|
294
|
+
continueLast = firstPrompt.launchMode === 'continue';
|
|
295
|
+
resumeSession = firstPrompt.launchMode === 'resume' ? resumeSession : '';
|
|
296
|
+
if (firstPrompt.launchMode === 'resume') {
|
|
297
|
+
const resumePrompt = await inquirer.prompt([
|
|
298
|
+
{
|
|
299
|
+
type: 'input',
|
|
300
|
+
name: 'resumeSession',
|
|
301
|
+
message: 'Claude session ID',
|
|
302
|
+
default: resumeSession,
|
|
303
|
+
validate: (value) => value.trim().length > 0 || 'Session ID is required',
|
|
304
|
+
},
|
|
305
|
+
]);
|
|
306
|
+
resumeSession = resumePrompt.resumeSession.trim();
|
|
307
|
+
}
|
|
308
|
+
if (firstPrompt.model === 'default') {
|
|
309
|
+
contextWindow = 'auto';
|
|
310
|
+
}
|
|
311
|
+
else if (selectedProfile.syntheticContextWindow) {
|
|
312
|
+
contextWindow = selectedProfile.syntheticContextWindow;
|
|
313
|
+
}
|
|
314
|
+
else if (!modelSupportsOneMillionContext(model)) {
|
|
315
|
+
contextWindow = '200k';
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
contextWindow = 'auto';
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
model,
|
|
322
|
+
contextWindow,
|
|
323
|
+
continueLast,
|
|
324
|
+
resumeSession: resumeSession || undefined,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
89
327
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
90
328
|
// PATTERN MATCHING
|
|
91
329
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -291,13 +529,6 @@ const isWindows = os.platform() === 'win32';
|
|
|
291
529
|
// Core ekkOS patches (eviction, context management) work with all recent versions
|
|
292
530
|
// Cosmetic patches may fail on newer versions but don't affect functionality
|
|
293
531
|
const PINNED_CLAUDE_VERSION = 'latest';
|
|
294
|
-
// Max output tokens for Claude responses
|
|
295
|
-
// Default: 128000 (Opus 4.6 / Sonnet 4.6 max output)
|
|
296
|
-
// Override via EKKOS_MAX_OUTPUT_TOKENS env var if needed
|
|
297
|
-
// Note: Claude Code has a known bug where CLAUDE_CODE_MAX_OUTPUT_TOKENS is ignored on Opus 4.6
|
|
298
|
-
// (see github.com/anthropics/claude-code/issues/24159). We set it anyway + the proxy also
|
|
299
|
-
// enforces this via max_tokens override on the API request.
|
|
300
|
-
const EKKOS_MAX_OUTPUT_TOKENS = process.env.EKKOS_MAX_OUTPUT_TOKENS || '128000';
|
|
301
532
|
const proxy_url_1 = require("../utils/proxy-url");
|
|
302
533
|
// Track proxy mode for getEkkosEnv (set by run() based on options)
|
|
303
534
|
let proxyModeEnabled = true;
|
|
@@ -318,12 +549,13 @@ let cliSessionName = null;
|
|
|
318
549
|
let cliSessionId = null;
|
|
319
550
|
/**
|
|
320
551
|
* Get environment with ekkOS enhancements
|
|
321
|
-
* - Sets CLAUDE_CODE_MAX_OUTPUT_TOKENS
|
|
552
|
+
* - Sets CLAUDE_CODE_MAX_OUTPUT_TOKENS dynamically for the selected Claude profile
|
|
322
553
|
* - Routes API through ekkOS proxy for seamless context eviction (when enabled)
|
|
323
554
|
* - Sets EKKOS_PROXY_MODE to signal JSONL rewriter to disable eviction
|
|
324
555
|
* - Passes session headers for eviction/retrieval context tracking
|
|
325
556
|
*/
|
|
326
557
|
function getEkkosEnv() {
|
|
558
|
+
const maxOutputTokens = resolveClaudeMaxOutputTokens(runtimeClaudeLaunchModel, runtimeClaudeContextWindow);
|
|
327
559
|
/* eslint-disable no-restricted-syntax -- System env spreading, not API key access */
|
|
328
560
|
const env = {
|
|
329
561
|
...process.env,
|
|
@@ -335,13 +567,18 @@ function getEkkosEnv() {
|
|
|
335
567
|
// Native autocompact would compact without ekkOS saving state first, causing knowledge loss.
|
|
336
568
|
// Only ekkOS-wrapped sessions get this; vanilla `claude` keeps autocompact on.
|
|
337
569
|
DISABLE_AUTO_COMPACT: 'true',
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
CLAUDE_CODE_MAX_OUTPUT_TOKENS: EKKOS_MAX_OUTPUT_TOKENS,
|
|
570
|
+
// Align Claude's advertised output ceiling with the chosen model/context profile.
|
|
571
|
+
// 1M Opus/Sonnet gets 128K; 200K Opus/Sonnet 4.5/4.6 is forced to 32,768.
|
|
572
|
+
// The proxy also enforces this server-side in case Claude Code ignores the env var.
|
|
573
|
+
CLAUDE_CODE_MAX_OUTPUT_TOKENS: maxOutputTokens,
|
|
343
574
|
};
|
|
344
575
|
/* eslint-enable no-restricted-syntax */
|
|
576
|
+
if (runtimeClaudeContextWindow === '200k') {
|
|
577
|
+
env.CLAUDE_CODE_DISABLE_1M_CONTEXT = '1';
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
delete env.CLAUDE_CODE_DISABLE_1M_CONTEXT;
|
|
581
|
+
}
|
|
345
582
|
// Check if proxy is disabled via env var or options
|
|
346
583
|
// eslint-disable-next-line no-restricted-syntax -- Feature flag, not API key
|
|
347
584
|
const proxyDisabled = process.env.EKKOS_DISABLE_PROXY === '1' || !proxyModeEnabled;
|
|
@@ -354,7 +591,9 @@ function getEkkosEnv() {
|
|
|
354
591
|
if (!cliSessionName) {
|
|
355
592
|
cliSessionId = crypto.randomUUID();
|
|
356
593
|
cliSessionName = (0, state_1.uuidToWords)(cliSessionId);
|
|
357
|
-
|
|
594
|
+
if (process.env.EKKOS_NO_SPLASH !== '1') {
|
|
595
|
+
console.log(chalk_1.default.gray(` 📂 Session: ${cliSessionName}`));
|
|
596
|
+
}
|
|
358
597
|
}
|
|
359
598
|
// Get full userId from config (NOT the truncated version from auth token)
|
|
360
599
|
// Config has full UUID like "d4532ba0-0a86-42ce-bab4-22aa62b55ce6"
|
|
@@ -374,7 +613,10 @@ function getEkkosEnv() {
|
|
|
374
613
|
// CRITICAL: Embed user/session in URL path since ANTHROPIC_HEADERS doesn't work
|
|
375
614
|
// Claude Code SDK doesn't forward custom headers, but it DOES use ANTHROPIC_BASE_URL
|
|
376
615
|
// Gateway extracts from URL: /proxy/{userId}/{sessionName}/v1/messages
|
|
377
|
-
const proxyUrl = (0, proxy_url_1.buildProxyUrl)(userId, cliSessionName, process.cwd(), cliSessionId
|
|
616
|
+
const proxyUrl = (0, proxy_url_1.buildProxyUrl)(userId, cliSessionName, process.cwd(), cliSessionId, {
|
|
617
|
+
claudeCodeVersion: runtimeClaudeCodeVersion || undefined,
|
|
618
|
+
claudeContextWindow: runtimeClaudeContextWindow,
|
|
619
|
+
});
|
|
378
620
|
env.ANTHROPIC_BASE_URL = proxyUrl;
|
|
379
621
|
// Proxy URL contains userId + project path — don't leak to terminal
|
|
380
622
|
}
|
|
@@ -585,6 +827,105 @@ function resolveGlobalClaudePath() {
|
|
|
585
827
|
function sleep(ms) {
|
|
586
828
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
587
829
|
}
|
|
830
|
+
function formatPulseLaunchMode(options) {
|
|
831
|
+
if (options.resumeSession)
|
|
832
|
+
return `RESUME // ${options.resumeSession}`;
|
|
833
|
+
if (options.continueLast)
|
|
834
|
+
return 'CONTINUE // most recent Claude thread';
|
|
835
|
+
return 'FRESH // new interactive shell';
|
|
836
|
+
}
|
|
837
|
+
function formatPulseModel(model) {
|
|
838
|
+
const normalized = (model || '').trim().toLowerCase();
|
|
839
|
+
if (!normalized)
|
|
840
|
+
return 'Claude default';
|
|
841
|
+
if (normalized === 'opus')
|
|
842
|
+
return 'Opus alias';
|
|
843
|
+
if (normalized === 'sonnet')
|
|
844
|
+
return 'Sonnet alias';
|
|
845
|
+
if (normalized === 'claude-opus-4-5')
|
|
846
|
+
return 'Claude Opus 4.5';
|
|
847
|
+
if (normalized === 'claude-opus-4-6')
|
|
848
|
+
return 'Claude Opus 4.6';
|
|
849
|
+
if (normalized === 'claude-sonnet-4-5')
|
|
850
|
+
return 'Claude Sonnet 4.5';
|
|
851
|
+
if (normalized === 'claude-sonnet-4-6')
|
|
852
|
+
return 'Claude Sonnet 4.6';
|
|
853
|
+
if (normalized === 'claude-haiku-4-5')
|
|
854
|
+
return 'Claude Haiku 4.5';
|
|
855
|
+
return model || 'Claude default';
|
|
856
|
+
}
|
|
857
|
+
function formatPulseContextLine(contextWindow) {
|
|
858
|
+
if (contextWindow === '200k') {
|
|
859
|
+
return '200K // 200,000 token window // forced 32,768 output';
|
|
860
|
+
}
|
|
861
|
+
if (contextWindow === '1m') {
|
|
862
|
+
return '1M // 1,000,000 token window // unlocked 128,000 output';
|
|
863
|
+
}
|
|
864
|
+
return 'AUTO // ekkOS resolves 200K vs 1M from the selected profile';
|
|
865
|
+
}
|
|
866
|
+
async function showPulseLaunchLoader(options) {
|
|
867
|
+
const cyan = chalk_1.default.hex('#00f0ff');
|
|
868
|
+
const amber = chalk_1.default.hex('#ffb800');
|
|
869
|
+
const green = chalk_1.default.hex('#00ff88');
|
|
870
|
+
const steel = chalk_1.default.hex('#7a7a8e');
|
|
871
|
+
const panel = chalk_1.default.hex('#111118');
|
|
872
|
+
const wordmark = [
|
|
873
|
+
'██████╗ ██╗ ██╗██╗ ███████╗███████╗',
|
|
874
|
+
'██╔══██╗██║ ██║██║ ██╔════╝██╔════╝',
|
|
875
|
+
'██████╔╝██║ ██║██║ ███████╗█████╗ ',
|
|
876
|
+
'██╔═══╝ ██║ ██║██║ ╚════██║██╔══╝ ',
|
|
877
|
+
'██║ ╚██████╔╝███████╗███████║███████╗',
|
|
878
|
+
'╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝',
|
|
879
|
+
];
|
|
880
|
+
const stages = [
|
|
881
|
+
'arming pulse launcher',
|
|
882
|
+
'syncing dashboard split',
|
|
883
|
+
'routing claude session',
|
|
884
|
+
];
|
|
885
|
+
const barWidth = 26;
|
|
886
|
+
const panelInnerWidth = 76;
|
|
887
|
+
const buildPanelLine = (labelColor, label, value) => {
|
|
888
|
+
const content = ` ${label.padEnd(7)} ${value}`;
|
|
889
|
+
const pad = Math.max(0, panelInnerWidth - stripAnsi(content).length);
|
|
890
|
+
return ` ${cyan('║')} ${labelColor(label.padEnd(7))} ${chalk_1.default.white(value)}${' '.repeat(pad)}${cyan('║')}`;
|
|
891
|
+
};
|
|
892
|
+
const headerContent = ' ekkOS.dev // PULSE BOOTSTRAP live launch matrix for Claude + dashboard';
|
|
893
|
+
const headerPad = Math.max(0, panelInnerWidth - headerContent.length);
|
|
894
|
+
console.log('');
|
|
895
|
+
for (const [idx, line] of wordmark.entries()) {
|
|
896
|
+
const color = idx % 2 === 0 ? cyan.bold : chalk_1.default.white.bold;
|
|
897
|
+
console.log(' ' + color(line));
|
|
898
|
+
await sleep(22);
|
|
899
|
+
}
|
|
900
|
+
console.log(' ' + amber('▔').repeat(54));
|
|
901
|
+
console.log(cyan(' ╔══════════════════════════════════════════════════════════════════════════════╗'));
|
|
902
|
+
console.log(` ${cyan('║')}${chalk_1.default.white.bold(' ekkOS.dev // PULSE BOOTSTRAP')}${steel(' live launch matrix for Claude + dashboard')}${' '.repeat(headerPad)}${cyan('║')}`);
|
|
903
|
+
console.log(buildPanelLine(green, 'MODE', formatPulseLaunchMode(options)));
|
|
904
|
+
console.log(buildPanelLine(green, 'MODEL', formatPulseModel(typeof options.model === 'string' ? options.model : undefined)));
|
|
905
|
+
console.log(buildPanelLine(amber, 'WINDOW', formatPulseContextLine(normalizeContextWindowOption(options.contextWindow))));
|
|
906
|
+
console.log(buildPanelLine(steel, 'PATH', 'selector -> dashboard -> claude -> proxy'));
|
|
907
|
+
console.log(cyan(' ╚══════════════════════════════════════════════════════════════════════════════╝'));
|
|
908
|
+
console.log(` ${green('200K')} ${steel('// Opus/Sonnet 4.5/4.6 => 32,768 output')}`);
|
|
909
|
+
console.log(` ${amber('1M ')} ${steel('// Opus/Sonnet 4.6 => 128,000 output')}`);
|
|
910
|
+
console.log('');
|
|
911
|
+
for (const stage of stages) {
|
|
912
|
+
for (let filled = 0; filled <= barWidth; filled += 1) {
|
|
913
|
+
const complete = '█'.repeat(filled);
|
|
914
|
+
const pending = '░'.repeat(barWidth - filled);
|
|
915
|
+
process.stdout.write(`\r ${amber('[')}${cyan(complete)}${steel(pending)}${amber(']')} ${chalk_1.default.white(stage)}`);
|
|
916
|
+
await sleep(16);
|
|
917
|
+
}
|
|
918
|
+
process.stdout.write('\n');
|
|
919
|
+
}
|
|
920
|
+
console.log(` ${green('READY')} ${panel('dashboard split armed')} ${steel('// selector handoff imminent')}`);
|
|
921
|
+
if (options.bypass) {
|
|
922
|
+
console.log(` ${amber('BYPASS')} ${chalk_1.default.white('permission prompts disabled for this launch')}`);
|
|
923
|
+
}
|
|
924
|
+
if (options.verbose) {
|
|
925
|
+
console.log(` ${steel('TRACE')} ${steel('verbose telemetry enabled')}`);
|
|
926
|
+
}
|
|
927
|
+
console.log('');
|
|
928
|
+
}
|
|
588
929
|
/**
|
|
589
930
|
* Show morning dreams with WOW factor right after sparkle animation.
|
|
590
931
|
* Colorful, lively, readable — real magic moment. Then pause for Enter.
|
|
@@ -844,6 +1185,15 @@ function launchWithDashboard(options) {
|
|
|
844
1185
|
runArgs.push('--skip-inject');
|
|
845
1186
|
if (options.noProxy)
|
|
846
1187
|
runArgs.push('--skip-proxy');
|
|
1188
|
+
if (options.continueLast)
|
|
1189
|
+
runArgs.push('--continue-last');
|
|
1190
|
+
if (options.resumeSession)
|
|
1191
|
+
runArgs.push('--resume-session', options.resumeSession);
|
|
1192
|
+
if (typeof options.model === 'string')
|
|
1193
|
+
runArgs.push('--model', options.model);
|
|
1194
|
+
if (options.contextWindow && options.contextWindow !== 'auto') {
|
|
1195
|
+
runArgs.push('--context-window', options.contextWindow);
|
|
1196
|
+
}
|
|
847
1197
|
const ekkosCmd = process.argv[1]; // Path to ekkos CLI
|
|
848
1198
|
const cwd = process.cwd();
|
|
849
1199
|
const termCols = process.stdout.columns ?? 160;
|
|
@@ -929,6 +1279,15 @@ function launchWithWindowsTerminal(options) {
|
|
|
929
1279
|
runArgs.push('--skip-inject');
|
|
930
1280
|
if (options.noProxy)
|
|
931
1281
|
runArgs.push('--skip-proxy');
|
|
1282
|
+
if (options.continueLast)
|
|
1283
|
+
runArgs.push('--continue-last');
|
|
1284
|
+
if (options.resumeSession)
|
|
1285
|
+
runArgs.push('--resume-session', options.resumeSession);
|
|
1286
|
+
if (typeof options.model === 'string')
|
|
1287
|
+
runArgs.push('--model', options.model);
|
|
1288
|
+
if (options.contextWindow && options.contextWindow !== 'auto') {
|
|
1289
|
+
runArgs.push('--context-window', options.contextWindow);
|
|
1290
|
+
}
|
|
932
1291
|
// Write a temp batch file to avoid all quoting issues
|
|
933
1292
|
// wt.exe doesn't resolve PATH for npm global bins — must use `cmd /c`
|
|
934
1293
|
const batPath = path.join(os.tmpdir(), `ekkos-wt-${Date.now()}.cmd`);
|
|
@@ -976,6 +1335,14 @@ async function run(options) {
|
|
|
976
1335
|
const verbose = options.verbose || false;
|
|
977
1336
|
const bypass = options.bypass || false;
|
|
978
1337
|
const noInject = options.noInject || false;
|
|
1338
|
+
const suppressPreClaudeOutput = process.env.EKKOS_NO_SPLASH === '1';
|
|
1339
|
+
const launchSelection = await resolveClaudeLaunchSelection(options);
|
|
1340
|
+
options.model = launchSelection.model;
|
|
1341
|
+
options.contextWindow = launchSelection.contextWindow;
|
|
1342
|
+
options.continueLast = launchSelection.continueLast;
|
|
1343
|
+
options.resumeSession = launchSelection.resumeSession;
|
|
1344
|
+
runtimeClaudeLaunchModel = launchSelection.model;
|
|
1345
|
+
runtimeClaudeContextWindow = launchSelection.contextWindow;
|
|
979
1346
|
// Honour -s flag: lock the session name before getEkkosEnv() can mint a new one.
|
|
980
1347
|
// This is critical for --dashboard mode where launchWithDashboard() pre-generates
|
|
981
1348
|
// a session name and passes it via -s so the dashboard and run command agree.
|
|
@@ -986,11 +1353,28 @@ async function run(options) {
|
|
|
986
1353
|
// Set proxy mode based on options (used by getEkkosEnv)
|
|
987
1354
|
proxyModeEnabled = !(options.noProxy || false);
|
|
988
1355
|
if (proxyModeEnabled) {
|
|
989
|
-
|
|
1356
|
+
if (!suppressPreClaudeOutput && !options.pulse) {
|
|
1357
|
+
console.log(chalk_1.default.cyan(' 🧠 ekkOS_Continuum Loaded!'));
|
|
1358
|
+
}
|
|
990
1359
|
}
|
|
991
|
-
else if (verbose) {
|
|
1360
|
+
else if (verbose && !suppressPreClaudeOutput) {
|
|
992
1361
|
console.log(chalk_1.default.yellow(' ⏭️ API proxy disabled (--no-proxy)'));
|
|
993
1362
|
}
|
|
1363
|
+
let pulseIntroShown = false;
|
|
1364
|
+
if (options.pulse
|
|
1365
|
+
&& process.env.EKKOS_REMOTE_SESSION !== '1'
|
|
1366
|
+
&& process.env.EKKOS_NO_SPLASH !== '1'
|
|
1367
|
+
&& process.stdout.isTTY === true) {
|
|
1368
|
+
await showPulseLaunchLoader({
|
|
1369
|
+
model: options.model,
|
|
1370
|
+
contextWindow: options.contextWindow,
|
|
1371
|
+
continueLast: options.continueLast,
|
|
1372
|
+
resumeSession: options.resumeSession,
|
|
1373
|
+
bypass: options.bypass,
|
|
1374
|
+
verbose: options.verbose,
|
|
1375
|
+
});
|
|
1376
|
+
pulseIntroShown = true;
|
|
1377
|
+
}
|
|
994
1378
|
// ══════════════════════════════════════════════════════════════════════════
|
|
995
1379
|
// DASHBOARD MODE: Launch via tmux (Unix) or Windows Terminal split pane
|
|
996
1380
|
// ══════════════════════════════════════════════════════════════════════════
|
|
@@ -1079,11 +1463,22 @@ async function run(options) {
|
|
|
1079
1463
|
const isNpxMode = rawClaudePath.startsWith('npx:');
|
|
1080
1464
|
const pinnedVersion = isNpxMode ? rawClaudePath.split(':')[1] : null;
|
|
1081
1465
|
const claudePath = isNpxMode ? 'npx' : rawClaudePath;
|
|
1466
|
+
runtimeClaudeCodeVersion = pinnedVersion || getClaudeVersion(claudePath);
|
|
1082
1467
|
// Build args early
|
|
1083
1468
|
const earlyArgs = [];
|
|
1084
1469
|
if (isNpxMode) {
|
|
1085
1470
|
earlyArgs.push(`@anthropic-ai/claude-code@${pinnedVersion}`);
|
|
1086
1471
|
}
|
|
1472
|
+
if (options.resumeSession) {
|
|
1473
|
+
earlyArgs.push('--resume', options.resumeSession);
|
|
1474
|
+
}
|
|
1475
|
+
else if (options.continueLast) {
|
|
1476
|
+
earlyArgs.push('--continue');
|
|
1477
|
+
}
|
|
1478
|
+
const launchModelArg = buildClaudeLaunchModelArg(options.model, options.contextWindow);
|
|
1479
|
+
if (launchModelArg) {
|
|
1480
|
+
earlyArgs.push('--model', launchModelArg);
|
|
1481
|
+
}
|
|
1087
1482
|
if (bypass) {
|
|
1088
1483
|
earlyArgs.push('--dangerously-skip-permissions');
|
|
1089
1484
|
}
|
|
@@ -1146,7 +1541,7 @@ async function run(options) {
|
|
|
1146
1541
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1147
1542
|
// STARTUP BANNER WITH COLOR PULSE ANIMATION
|
|
1148
1543
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1149
|
-
const skipFancyIntro = process.env.EKKOS_REMOTE_SESSION === '1' ||
|
|
1544
|
+
const skipFancyIntro = pulseIntroShown || process.env.EKKOS_REMOTE_SESSION === '1' || suppressPreClaudeOutput || !!process.env.TMUX;
|
|
1150
1545
|
if (!skipFancyIntro) {
|
|
1151
1546
|
const logoLines = [
|
|
1152
1547
|
' ▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄ ▄▄ ▄▄',
|
|
@@ -1328,7 +1723,7 @@ async function run(options) {
|
|
|
1328
1723
|
}
|
|
1329
1724
|
console.log('');
|
|
1330
1725
|
}
|
|
1331
|
-
else {
|
|
1726
|
+
else if (!suppressPreClaudeOutput) {
|
|
1332
1727
|
// Static banner for Windows / remote / no-splash — no cursor manipulation
|
|
1333
1728
|
console.log('');
|
|
1334
1729
|
console.log(chalk_1.default.hex('#FF6B35').bold(' ekkOS_Pulse') + chalk_1.default.gray(' — Context is finite. Intelligence isn\'t.'));
|
|
@@ -1344,7 +1739,9 @@ async function run(options) {
|
|
|
1344
1739
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1345
1740
|
// MAGIC MOMENT: Morning dreams right after sparkle, before Claude appears
|
|
1346
1741
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1347
|
-
|
|
1742
|
+
if (!suppressPreClaudeOutput && !options.pulse) {
|
|
1743
|
+
await showMorningDreamsIfNeeded();
|
|
1744
|
+
}
|
|
1348
1745
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1349
1746
|
// ANIMATION COMPLETE: Mark ready and flush buffered Claude output
|
|
1350
1747
|
// ══════════════════════════════════════════════════════════════════════════
|
|
@@ -1835,9 +2232,21 @@ async function run(options) {
|
|
|
1835
2232
|
}
|
|
1836
2233
|
if (verbose) {
|
|
1837
2234
|
// Show Claude version
|
|
1838
|
-
const ccVersion = pinnedVersion || PINNED_CLAUDE_VERSION;
|
|
2235
|
+
const ccVersion = runtimeClaudeCodeVersion || pinnedVersion || PINNED_CLAUDE_VERSION;
|
|
1839
2236
|
const versionStr = `Claude Code v${ccVersion}`;
|
|
1840
2237
|
console.log(chalk_1.default.gray(` 🤖 ${versionStr}`));
|
|
2238
|
+
if (options.model) {
|
|
2239
|
+
console.log(chalk_1.default.gray(` 🧩 Model: ${buildClaudeLaunchModelArg(options.model, options.contextWindow)}`));
|
|
2240
|
+
}
|
|
2241
|
+
if (options.contextWindow && options.contextWindow !== 'auto') {
|
|
2242
|
+
console.log(chalk_1.default.gray(` 🪟 Context: ${options.contextWindow}`));
|
|
2243
|
+
}
|
|
2244
|
+
if (options.continueLast) {
|
|
2245
|
+
console.log(chalk_1.default.gray(' ↩ Continue: most recent Claude conversation'));
|
|
2246
|
+
}
|
|
2247
|
+
if (options.resumeSession) {
|
|
2248
|
+
console.log(chalk_1.default.gray(` ↪ Resume: ${options.resumeSession}`));
|
|
2249
|
+
}
|
|
1841
2250
|
if (currentSession) {
|
|
1842
2251
|
console.log(chalk_1.default.green(` 📍 Session: ${currentSession}`));
|
|
1843
2252
|
}
|