@blockrun/franklin 3.10.0 → 3.10.2
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/start.d.ts +4 -0
- package/dist/commands/start.js +57 -5
- package/dist/index.js +12 -2
- package/dist/panel/html.js +501 -23
- package/dist/panel/server.js +127 -0
- package/dist/session/from-import.d.ts +18 -0
- package/dist/session/from-import.js +553 -0
- package/dist/stats/tracker.d.ts +4 -0
- package/dist/stats/tracker.js +30 -4
- package/dist/ui/app.js +6 -12
- package/package.json +1 -1
package/dist/commands/start.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ interface StartOptions {
|
|
|
4
4
|
debug?: boolean;
|
|
5
5
|
trust?: boolean;
|
|
6
6
|
version?: string;
|
|
7
|
+
/** Start a new Franklin session seeded from another agent's saved context. */
|
|
8
|
+
from?: string;
|
|
9
|
+
/** Optional external agent session id/path for --from. If omitted, show a picker. */
|
|
10
|
+
fromSessionId?: string;
|
|
7
11
|
/** Resume: explicit session ID, or true for "most recent in cwd", or 'picker' to prompt */
|
|
8
12
|
resume?: string | boolean | 'picker';
|
|
9
13
|
/** Continue: resume most recent session matching the current working directory */
|
package/dist/commands/start.js
CHANGED
|
@@ -81,7 +81,48 @@ export async function startCommand(options) {
|
|
|
81
81
|
// old nvidia-nemotron default, which stubbed tool use.
|
|
82
82
|
model = 'blockrun/auto';
|
|
83
83
|
}
|
|
84
|
-
|
|
84
|
+
let workDir = process.cwd();
|
|
85
|
+
let importedKickoffPrompt;
|
|
86
|
+
if (options.from) {
|
|
87
|
+
const { importExternalSessionAsFranklin, parseExternalAgentSource } = await import('../session/from-import.js');
|
|
88
|
+
const source = parseExternalAgentSource(options.from);
|
|
89
|
+
if (!source) {
|
|
90
|
+
console.error(chalk.red(`Unknown --from source: ${options.from}`));
|
|
91
|
+
console.error(chalk.dim('Supported sources: claude, codex'));
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const imported = await importExternalSessionAsFranklin(source, options.fromSessionId, { model, workDir });
|
|
97
|
+
if (imported.imported.cwd) {
|
|
98
|
+
try {
|
|
99
|
+
process.chdir(imported.imported.cwd);
|
|
100
|
+
workDir = process.cwd();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Keep the caller's cwd if the source session directory no longer exists.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
options.resume = imported.sessionId;
|
|
107
|
+
options.continue = false;
|
|
108
|
+
importedKickoffPrompt = [
|
|
109
|
+
`Continue from the imported ${source} handoff context.`,
|
|
110
|
+
'Briefly explain what you understand the previous session was working on, what state it appears to be in, and the most likely next step.',
|
|
111
|
+
'Do not claim you resumed or modified the source agent session. This is a new Franklin session with imported context awareness.',
|
|
112
|
+
'If the next action is clear, offer to proceed; if it is not clear, ask one concise question.',
|
|
113
|
+
].join('\n');
|
|
114
|
+
console.log(chalk.green(` Imported ${source} context into Franklin session ${imported.sessionId.slice(0, 24)}…`));
|
|
115
|
+
console.log(chalk.dim(` Source session: ${imported.imported.id}`));
|
|
116
|
+
if (imported.imported.cwd)
|
|
117
|
+
console.log(chalk.dim(` Dir: ${workDir}`));
|
|
118
|
+
console.log('');
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.error(chalk.red(err.message));
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
85
126
|
// --prompt batch mode: skip all interactive startup UI/side effects so
|
|
86
127
|
// stdout stays clean for scripts and one-shot callers. Keep the capability surface to the
|
|
87
128
|
// built-ins only — no panel, no MCP autoconnect, no wallet/banner chatter.
|
|
@@ -314,10 +355,10 @@ export async function startCommand(options) {
|
|
|
314
355
|
if (process.stdin.isTTY) {
|
|
315
356
|
await runWithInkUI(agentConfig, model, workDir, version, walletInfo, (cb) => {
|
|
316
357
|
onBalanceFetched = cb;
|
|
317
|
-
}, fetchBalance);
|
|
358
|
+
}, fetchBalance, importedKickoffPrompt);
|
|
318
359
|
}
|
|
319
360
|
else {
|
|
320
|
-
await runWithBasicUI(agentConfig, model, workDir);
|
|
361
|
+
await runWithBasicUI(agentConfig, model, workDir, importedKickoffPrompt);
|
|
321
362
|
}
|
|
322
363
|
}
|
|
323
364
|
// ─── One-shot mode (franklin --prompt "...") ──────────────────────────────
|
|
@@ -348,7 +389,7 @@ async function runOneShot(agentConfig, prompt) {
|
|
|
348
389
|
return exitCode;
|
|
349
390
|
}
|
|
350
391
|
// ─── Ink UI (interactive terminal) ─────────────────────────────────────────
|
|
351
|
-
async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, onBalanceReady, fetchBalance) {
|
|
392
|
+
async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, onBalanceReady, fetchBalance, initialInput) {
|
|
352
393
|
const startSnapshot = snapshotStats();
|
|
353
394
|
const ui = launchInkUI({
|
|
354
395
|
model,
|
|
@@ -381,8 +422,13 @@ async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, on
|
|
|
381
422
|
});
|
|
382
423
|
}
|
|
383
424
|
let sessionHistory;
|
|
425
|
+
let deliveredInitialInput = false;
|
|
384
426
|
try {
|
|
385
427
|
sessionHistory = await interactiveSession(agentConfig, async () => {
|
|
428
|
+
if (initialInput && !deliveredInitialInput) {
|
|
429
|
+
deliveredInitialInput = true;
|
|
430
|
+
return initialInput;
|
|
431
|
+
}
|
|
386
432
|
const input = await ui.waitForInput();
|
|
387
433
|
if (input === null)
|
|
388
434
|
return null;
|
|
@@ -441,14 +487,20 @@ async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, on
|
|
|
441
487
|
console.log(chalk.dim('\nGoodbye.\n'));
|
|
442
488
|
}
|
|
443
489
|
// ─── Basic readline UI (piped input) ───────────────────────────────────────
|
|
444
|
-
async function runWithBasicUI(agentConfig, model, workDir) {
|
|
490
|
+
async function runWithBasicUI(agentConfig, model, workDir, initialInput) {
|
|
445
491
|
const { TerminalUI } = await import('../ui/terminal.js');
|
|
446
492
|
const ui = new TerminalUI();
|
|
447
493
|
ui.printWelcome(model, workDir);
|
|
448
494
|
const startSnapshot = snapshotStats();
|
|
449
495
|
let lastTerminalPrompt = '';
|
|
496
|
+
let deliveredInitialInput = false;
|
|
450
497
|
try {
|
|
451
498
|
await interactiveSession(agentConfig, async () => {
|
|
499
|
+
if (initialInput && !deliveredInitialInput) {
|
|
500
|
+
deliveredInitialInput = true;
|
|
501
|
+
lastTerminalPrompt = initialInput;
|
|
502
|
+
return initialInput;
|
|
503
|
+
}
|
|
452
504
|
while (true) {
|
|
453
505
|
const input = await ui.promptUser();
|
|
454
506
|
if (input === null)
|
package/dist/index.js
CHANGED
|
@@ -38,15 +38,17 @@ program
|
|
|
38
38
|
.action((chain) => setupCommand(chain));
|
|
39
39
|
program
|
|
40
40
|
.command('start')
|
|
41
|
+
.argument('[fromSessionId]', 'External agent session id/path for --from')
|
|
41
42
|
.description('Start the franklin agent')
|
|
42
43
|
.option('-m, --model <model>', 'Model to use (e.g. openai/gpt-5.5, anthropic/claude-sonnet-4.6). Default from config or claude-sonnet-4.6')
|
|
43
44
|
.option('--debug', 'Enable debug logging')
|
|
44
45
|
.option('--trust', 'Trust mode — skip permission prompts for all tools')
|
|
46
|
+
.option('--from <agent>', 'Start a new Franklin session from another agent context (claude or codex)')
|
|
45
47
|
.option('-r, --resume [sessionId]', 'Resume a session by ID (or show picker if omitted)')
|
|
46
48
|
.option('-c, --continue', 'Continue the most recent session in this directory')
|
|
47
49
|
.option('--max-spend <usd>', 'Hard USD cap on total session API spend — session stops when exceeded')
|
|
48
50
|
.option('-p, --prompt <text>', 'Run a single prompt non-interactively (for batch/scripted use)')
|
|
49
|
-
.action((options) => startCommand({ ...options, version }));
|
|
51
|
+
.action((fromSessionId, options) => startCommand({ ...options, fromSessionId, version }));
|
|
50
52
|
program
|
|
51
53
|
.command('resume [sessionId]')
|
|
52
54
|
.description('Resume a saved Franklin session (alias for: franklin --resume)')
|
|
@@ -236,7 +238,7 @@ const args = process.argv.slice(2);
|
|
|
236
238
|
const firstArg = args[0];
|
|
237
239
|
const HELP_FLAGS = new Set(['-h', '--help']);
|
|
238
240
|
const VERSION_FLAGS = new Set(['-V', '--version']);
|
|
239
|
-
const START_ONLY_FLAGS = new Set(['--trust', '--debug', '-m', '--model', '-r', '--resume', '-c', '--continue', '-p', '--prompt', '--max-spend']);
|
|
241
|
+
const START_ONLY_FLAGS = new Set(['--trust', '--debug', '-m', '--model', '--from', '-r', '--resume', '-c', '--continue', '-p', '--prompt', '--max-spend']);
|
|
240
242
|
function hasAnyFlag(argv, flags) {
|
|
241
243
|
return argv.some(arg => flags.has(arg));
|
|
242
244
|
}
|
|
@@ -260,6 +262,14 @@ function parseStartFlags(argv, startIdx = 0) {
|
|
|
260
262
|
else if (arg === '--max-spend' && argv[i + 1]) {
|
|
261
263
|
opts.maxSpend = argv[++i];
|
|
262
264
|
}
|
|
265
|
+
else if (arg === '--from') {
|
|
266
|
+
opts.from = argv[i + 1] && !argv[i + 1].startsWith('-') ? argv[++i] : '';
|
|
267
|
+
const next = argv[i + 1];
|
|
268
|
+
if (next && !next.startsWith('-')) {
|
|
269
|
+
opts.fromSessionId = next;
|
|
270
|
+
i++;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
263
273
|
else if (arg === '-c' || arg === '--continue') {
|
|
264
274
|
opts.continue = true;
|
|
265
275
|
}
|