@runtypelabs/cli 0.2.0 → 0.2.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/index.js CHANGED
@@ -231,91 +231,184 @@ var CallbackServer = class {
231
231
  }
232
232
  }
233
233
  successHTML() {
234
- return `
235
- <!DOCTYPE html>
236
- <html>
237
- <head>
238
- <title>Authentication Successful</title>
239
- <style>
240
- * {
241
- margin: 0;
242
- padding: 0;
243
- box-sizing: border-box;
244
- }
245
- body {
246
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
247
- display: flex;
248
- align-items: center;
249
- justify-content: center;
250
- height: 100vh;
251
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
252
- }
253
- .container {
254
- background: white;
255
- padding: 3rem;
256
- border-radius: 12px;
257
- text-align: center;
258
- box-shadow: 0 20px 40px rgba(0,0,0,0.1);
259
- max-width: 400px;
260
- animation: slideUp 0.3s ease-out;
261
- }
262
- @keyframes slideUp {
263
- from {
264
- opacity: 0;
265
- transform: translateY(20px);
266
- }
267
- to {
268
- opacity: 1;
269
- transform: translateY(0);
270
- }
271
- }
272
- .checkmark {
273
- color: #10b981;
274
- font-size: 64px;
275
- margin-bottom: 1rem;
276
- animation: scaleIn 0.3s ease-out 0.1s both;
277
- }
278
- @keyframes scaleIn {
279
- from {
280
- transform: scale(0);
281
- }
282
- to {
283
- transform: scale(1);
284
- }
285
- }
286
- h1 {
287
- color: #1f2937;
288
- margin-bottom: 0.5rem;
289
- font-size: 1.5rem;
290
- }
291
- p {
292
- color: #6b7280;
293
- font-size: 1rem;
294
- }
295
- .progress {
296
- margin-top: 1.5rem;
297
- color: #9ca3af;
298
- font-size: 0.875rem;
299
- }
300
- </style>
301
- </head>
302
- <body>
303
- <div class="container">
304
- <div class="checkmark">\u2713</div>
305
- <h1>Authentication Successful!</h1>
306
- <p>You can close this window and return to your terminal.</p>
307
- <p class="progress">Closing automatically in 3 seconds...</p>
308
- <script>
309
- setTimeout(() => {
310
- window.close();
311
- // Fallback if window.close() doesn't work
312
- document.body.innerHTML = '<div class="container"><h1>You can now close this window</h1></div>';
313
- }, 3000);
314
- </script>
315
- </div>
316
- </body>
317
- </html>
318
- `;
234
+ return `<!DOCTYPE html>
235
+ <html lang="en">
236
+ <head>
237
+ <meta charset="utf-8">
238
+ <title>Authentication Successful</title>
239
+ <style>
240
+ * { margin: 0; padding: 0; box-sizing: border-box; }
241
+ body {
242
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
243
+ height: 100vh;
244
+ overflow: hidden;
245
+ background: #0a0a0a;
246
+ }
247
+ .grid-bg {
248
+ position: absolute;
249
+ inset: 0;
250
+ z-index: 0;
251
+ overflow: hidden;
252
+ }
253
+ .grid-bg .gradient {
254
+ position: absolute;
255
+ inset: 0;
256
+ background: linear-gradient(to bottom, #0a0a0a 0%, #171717 50%, #262626 100%);
257
+ }
258
+ @keyframes gridMove {
259
+ 0% { background-position-y: 0; }
260
+ 100% { background-position-y: 30px; }
261
+ }
262
+ .grid-bg .grid-vertical-wrap {
263
+ position: absolute;
264
+ left: 0;
265
+ right: 0;
266
+ bottom: 0;
267
+ top: 40%;
268
+ transform-origin: top center;
269
+ transform: perspective(500px) rotateX(60deg);
270
+ }
271
+ .grid-bg .grid-vertical-wrap .fade {
272
+ position: absolute;
273
+ inset: 0;
274
+ background: linear-gradient(to bottom, transparent 0%, rgba(255,255,255,0.02) 100%);
275
+ }
276
+ .grid-bg .grid-vertical-wrap .lines {
277
+ position: absolute;
278
+ inset: 0;
279
+ background-image: repeating-linear-gradient(
280
+ 90deg,
281
+ transparent 0,
282
+ transparent 49px,
283
+ rgba(255,255,255,0.08) 49px,
284
+ rgba(255,255,255,0.08) 50px
285
+ );
286
+ background-size: 50px 100%;
287
+ }
288
+ .grid-bg .grid-horizontal {
289
+ position: absolute;
290
+ left: 0;
291
+ right: 0;
292
+ bottom: 0;
293
+ top: 40%;
294
+ transform-origin: top center;
295
+ transform: perspective(500px) rotateX(60deg);
296
+ background-image: repeating-linear-gradient(
297
+ 0deg,
298
+ transparent 0,
299
+ transparent 29px,
300
+ rgba(255,255,255,0.06) 29px,
301
+ rgba(255,255,255,0.06) 30px
302
+ );
303
+ background-size: 100% 30px;
304
+ animation: gridMove 2s linear infinite;
305
+ }
306
+ .card-wrap {
307
+ position: absolute;
308
+ left: 50%;
309
+ top: 50%;
310
+ transform: translate(-50%, -50%);
311
+ z-index: 10;
312
+ width: 100%;
313
+ max-width: 400px;
314
+ perspective: 1400px;
315
+ animation: cardIn 0.4s ease-out;
316
+ }
317
+ @keyframes cardIn {
318
+ from { opacity: 0; transform: translate(-50%, -50%) translateY(16px); }
319
+ to { opacity: 1; transform: translate(-50%, -50%) translateY(0); }
320
+ }
321
+ .card-inner {
322
+ position: relative;
323
+ }
324
+ .card-layer-back,
325
+ .card-layer-mid {
326
+ position: absolute;
327
+ inset: 0;
328
+ border-radius: 12px;
329
+ border: 1px solid #e5e7eb;
330
+ pointer-events: none;
331
+ }
332
+ .card-layer-back {
333
+ background: rgba(0, 0, 0, 0.08);
334
+ transform: translate3d(0, -18px, -36px) rotateX(6deg);
335
+ }
336
+ .card-layer-mid {
337
+ background: rgba(0, 0, 0, 0.05);
338
+ transform: translate3d(0, -10px, -20px) rotateX(3deg);
339
+ }
340
+ .card-front {
341
+ position: relative;
342
+ border-radius: 12px;
343
+ border: 1px solid rgba(255, 255, 255, 1);
344
+ background: rgba(0, 0, 0, .5);
345
+ backdrop-filter: blur(10px);
346
+ -webkit-backdrop-filter: blur(10px);
347
+ padding: 2rem 2.5rem;
348
+ text-align: center;
349
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
350
+ transform: rotateX(-10deg) translateZ(0);
351
+ transform-style: preserve-3d;
352
+ }
353
+ .card-front .section-title {
354
+ color: #ffffff;
355
+ font-size: 0.8125rem;
356
+ font-weight: 600;
357
+ letter-spacing: 0.05em;
358
+ text-transform: uppercase;
359
+ margin-bottom: 1rem;
360
+ }
361
+ .card-front .flow-arrow {
362
+ color: rgba(255, 255, 255, 0.7);
363
+ font-size: 1rem;
364
+ margin-bottom: 1rem;
365
+ line-height: 1;
366
+ }
367
+ .card-front .message {
368
+ color: #ffffff;
369
+ font-size: 0.75rem;
370
+ font-weight: 100;
371
+ margin-bottom: 0.5rem;
372
+ font-family: monospace;
373
+ }
374
+ .card-front .progress {
375
+ color: rgba(255, 255, 255, 0.75);
376
+ font-size: 0.8125rem;
377
+ }
378
+ </style>
379
+ </head>
380
+ <body>
381
+ <div class="grid-bg">
382
+ <div class="gradient"></div>
383
+ <div class="grid-vertical-wrap">
384
+ <div class="fade"></div>
385
+ <div class="lines"></div>
386
+ </div>
387
+ <div class="grid-horizontal"></div>
388
+ </div>
389
+ <div id="card-wrap" class="card-wrap">
390
+ <div class="card-inner">
391
+ <div class="card-layer-back" aria-hidden="true"></div>
392
+ <div class="card-layer-mid" aria-hidden="true"></div>
393
+ <div id="card" class="card-front">
394
+ <div class="section-title">Authentication Successful</div>
395
+ <div class="flow-arrow" aria-hidden="true">\u2193</div>
396
+ <p class="message">You can close this window and return to your terminal.</p>
397
+ <p class="progress" id="progress">Closing automatically in 3 seconds\u2026</p>
398
+ </div>
399
+ </div>
400
+ </div>
401
+ <script>
402
+ setTimeout(function() {
403
+ window.close();
404
+ var prog = document.getElementById('progress');
405
+ if (prog) prog.textContent = 'You can now close this window.';
406
+ var card = document.getElementById('card');
407
+ if (card) card.innerHTML = '<div class="section-title">Runtype is Ready</div><p class="message">You can now close this window</p>';
408
+ }, 3000);
409
+ </script>
410
+ </body>
411
+ </html>`;
319
412
  }
320
413
  errorHTML(error) {
321
414
  return `
@@ -469,10 +562,10 @@ var isAuthMeResponse = (value) => {
469
562
  return typeof record.user_id === "string";
470
563
  };
471
564
  var ApiKeyManager = class {
472
- async exchangeSessionForApiKey(authCode, apiUrl) {
565
+ async exchangeSessionForApiKey(authCode, apiUrl, dashboardUrl) {
473
566
  void apiUrl;
474
- const dashboardUrl = process.env.RUNTYPE_DASHBOARD_URL || "http://localhost:3001";
475
- const authResponse = await fetch(`${dashboardUrl}/api/cli/auth?code=${authCode}`, {
567
+ const base = dashboardUrl ?? getDashboardUrl();
568
+ const authResponse = await fetch(`${base.replace(/\/$/, "")}/api/cli/auth?code=${encodeURIComponent(authCode)}`, {
476
569
  method: "GET",
477
570
  headers: {
478
571
  "Content-Type": "application/json"
@@ -544,7 +637,8 @@ authCommand.command("signup").description("Create a new Runtype account").option
544
637
  const apiKeyManager = new ApiKeyManager();
545
638
  const { key, userId, orgId } = await apiKeyManager.exchangeSessionForApiKey(
546
639
  sessionToken,
547
- options.apiUrl || getApiUrl()
640
+ options.apiUrl || getApiUrl(),
641
+ options.dashboardUrl || getDashboardUrl()
548
642
  );
549
643
  spinner.text = "Storing credentials securely...";
550
644
  const store = new CredentialStore();
@@ -610,7 +704,8 @@ authCommand.command("login").description("Login to existing account").option("--
610
704
  const apiKeyManager = new ApiKeyManager();
611
705
  const { key, userId, orgId } = await apiKeyManager.exchangeSessionForApiKey(
612
706
  sessionToken,
613
- options.apiUrl || getApiUrl()
707
+ options.apiUrl || getApiUrl(),
708
+ options.dashboardUrl || getDashboardUrl()
614
709
  );
615
710
  spinner.text = "Storing credentials...";
616
711
  await store.saveCredentials({
@@ -886,7 +981,8 @@ async function handleBrowserLogin(store, apiUrl) {
886
981
  const apiKeyManager = new ApiKeyManager();
887
982
  const { key, userId, orgId } = await apiKeyManager.exchangeSessionForApiKey(
888
983
  sessionToken,
889
- apiUrl || getApiUrl()
984
+ apiUrl || getApiUrl(),
985
+ getDashboardUrl()
890
986
  );
891
987
  spinner.text = "Storing credentials...";
892
988
  await store.saveCredentials({
@@ -2500,7 +2596,8 @@ async function handleBrowserAuth(store, mode, apiUrl) {
2500
2596
  const apiKeyManager = new ApiKeyManager();
2501
2597
  const { key, userId, orgId } = await apiKeyManager.exchangeSessionForApiKey(
2502
2598
  sessionToken,
2503
- apiUrl || getApiUrl()
2599
+ apiUrl || getApiUrl(),
2600
+ getDashboardUrl()
2504
2601
  );
2505
2602
  await store.saveCredentials({
2506
2603
  apiKey: key,
@@ -2812,6 +2909,205 @@ function saveState(filePath, state) {
2812
2909
  fs3.mkdirSync(dir, { recursive: true });
2813
2910
  fs3.writeFileSync(filePath, JSON.stringify(state, null, 2));
2814
2911
  }
2912
+ function isRecord(value) {
2913
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2914
+ }
2915
+ function parseSandboxProvider(value) {
2916
+ if (!value) return void 0;
2917
+ const normalized = value.trim().toLowerCase();
2918
+ if (normalized === "quickjs" || normalized === "daytona") return normalized;
2919
+ return void 0;
2920
+ }
2921
+ function parseSandboxLanguage(provider, value) {
2922
+ if (provider === "quickjs") return "javascript";
2923
+ if (typeof value === "string") {
2924
+ const normalized = value.trim().toLowerCase();
2925
+ if (normalized === "javascript" || normalized === "typescript" || normalized === "python") {
2926
+ return normalized;
2927
+ }
2928
+ }
2929
+ return "javascript";
2930
+ }
2931
+ function parseSandboxTimeout(value, provider) {
2932
+ const fallback = provider === "quickjs" ? 5e3 : 3e4;
2933
+ let numericValue;
2934
+ if (typeof value === "number" && Number.isFinite(value)) {
2935
+ numericValue = value;
2936
+ } else if (typeof value === "string" && value.trim() !== "") {
2937
+ const parsed = Number(value);
2938
+ if (Number.isFinite(parsed)) {
2939
+ numericValue = parsed;
2940
+ }
2941
+ }
2942
+ if (numericValue === void 0) return fallback;
2943
+ return Math.max(1, Math.min(3e4, Math.floor(numericValue)));
2944
+ }
2945
+ function parseDaytonaExecutionResult(value) {
2946
+ if (typeof value !== "string") return value;
2947
+ const trimmed = value.trim();
2948
+ if (!trimmed) return "";
2949
+ try {
2950
+ return JSON.parse(trimmed);
2951
+ } catch {
2952
+ const lines = trimmed.split("\n").map((line) => line.trim()).filter(Boolean);
2953
+ const lastLine = lines[lines.length - 1];
2954
+ if (!lastLine) return value;
2955
+ try {
2956
+ return JSON.parse(lastLine);
2957
+ } catch {
2958
+ return value;
2959
+ }
2960
+ }
2961
+ }
2962
+ function createSandboxInstructions(provider) {
2963
+ if (provider === "quickjs") {
2964
+ return [
2965
+ "--- Sandbox Tooling (QuickJS) ---",
2966
+ "You can execute JavaScript snippets with the local tool `run_sandbox_code`.",
2967
+ "Call shape:",
2968
+ '{ "code": "...", "parameters": { ... }, "timeoutMs": 5000 }',
2969
+ "QuickJS rules:",
2970
+ "1. Use JavaScript only (no TypeScript or Python).",
2971
+ "2. Inputs are passed in the `parameters` object (for example: `const x = parameters.x`).",
2972
+ "3. The snippet is wrapped in a function. Use top-level `return ...` to produce the result.",
2973
+ "4. Return JSON-serializable values (object, array, string, number, boolean, null).",
2974
+ "5. No Node/Bun/Deno APIs, imports/require, process, filesystem, or network calls.",
2975
+ "Example:",
2976
+ "const nums = parameters.nums || []",
2977
+ "const sum = nums.reduce((acc, n) => acc + n, 0)",
2978
+ "return { sum, count: nums.length }"
2979
+ ].join("\n");
2980
+ }
2981
+ return [
2982
+ "--- Sandbox Tooling (Daytona) ---",
2983
+ "You can execute code snippets with the local tool `run_sandbox_code`.",
2984
+ "Call shape:",
2985
+ '{ "code": "...", "parameters": { ... }, "language": "javascript|typescript|python", "timeoutMs": 30000 }',
2986
+ "Daytona rules:",
2987
+ "1. Choose one language: javascript, typescript, or python.",
2988
+ "2. Parameters are injected as top-level variables before your code (do not rely on `parameters`).",
2989
+ "3. Your snippet runs as a full program. Do not use top-level `return`.",
2990
+ "4. For structured results, write a single JSON value to stdout as the final output.",
2991
+ "5. JS/TS: `console.log(JSON.stringify({ ... }))`; Python: `import json` then `print(json.dumps({ ... }))`.",
2992
+ "6. Avoid extra logs before the final JSON line, or parsing may fail."
2993
+ ].join("\n");
2994
+ }
2995
+ function buildResumeCommand(agent, message, options, parsedSandbox) {
2996
+ const nameFlag = options.name ? ` --name ${options.name}` : "";
2997
+ const sandboxFlag = parsedSandbox ? ` --sandbox ${parsedSandbox}` : "";
2998
+ return `runtype marathon ${agent} -m "${message}" --resume${nameFlag}${sandboxFlag}`;
2999
+ }
3000
+ function createSandboxLocalTool(client, provider, debugMode) {
3001
+ return {
3002
+ description: provider === "quickjs" ? "Execute JavaScript code in QuickJS sandbox. Inputs are passed via parameters object." : "Execute JavaScript/TypeScript/Python code in Daytona sandbox. Inputs are injected as top-level variables.",
3003
+ parametersSchema: {
3004
+ type: "object",
3005
+ properties: {
3006
+ code: { type: "string", description: "Code snippet to execute" },
3007
+ parameters: {
3008
+ type: "object",
3009
+ description: "Input parameters for the code (JSON object)"
3010
+ },
3011
+ language: {
3012
+ type: "string",
3013
+ enum: provider === "quickjs" ? ["javascript"] : ["javascript", "typescript", "python"],
3014
+ description: provider === "quickjs" ? "QuickJS only accepts javascript" : "Daytona code language"
3015
+ },
3016
+ timeoutMs: { type: "number", description: "Execution timeout in ms (max 30000)" }
3017
+ },
3018
+ required: ["code"]
3019
+ },
3020
+ execute: async (args) => {
3021
+ const rawCode = args.code;
3022
+ const code = typeof rawCode === "string" ? rawCode : "";
3023
+ if (!code.trim()) {
3024
+ return { success: false, error: "code is required" };
3025
+ }
3026
+ const language = parseSandboxLanguage(provider, args.language);
3027
+ const timeout = parseSandboxTimeout(args.timeoutMs, provider);
3028
+ const parameters = isRecord(args.parameters) ? args.parameters : {};
3029
+ const gateDecision = (0, import_sdk5.evaluateGeneratedRuntimeToolProposal)(
3030
+ {
3031
+ name: "run_sandbox_code",
3032
+ description: `Execute code in ${provider}`,
3033
+ toolType: "custom",
3034
+ parametersSchema: { type: "object" },
3035
+ config: {
3036
+ code,
3037
+ timeout,
3038
+ sandboxProvider: provider,
3039
+ language
3040
+ }
3041
+ },
3042
+ {
3043
+ allowedToolTypes: ["custom"],
3044
+ allowedSandboxProviders: [provider],
3045
+ allowedLanguages: provider === "quickjs" ? ["javascript"] : ["javascript", "typescript", "python"],
3046
+ maxTimeoutMs: 3e4,
3047
+ maxCodeLength: 12e3
3048
+ }
3049
+ );
3050
+ if (!gateDecision.approved) {
3051
+ return {
3052
+ success: false,
3053
+ error: gateDecision.reason,
3054
+ violations: gateDecision.violations
3055
+ };
3056
+ }
3057
+ const tempToolName = `marathon_${provider}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
3058
+ let tempToolId;
3059
+ try {
3060
+ const createRequest = {
3061
+ name: tempToolName,
3062
+ description: `Ephemeral ${provider} sandbox execution`,
3063
+ toolType: "custom",
3064
+ parametersSchema: { type: "object", properties: {} },
3065
+ config: {
3066
+ code,
3067
+ timeout,
3068
+ allowedApis: [],
3069
+ sandboxProvider: provider,
3070
+ language
3071
+ }
3072
+ };
3073
+ const created = await client.tools.create(createRequest);
3074
+ tempToolId = created.id;
3075
+ const executeRequest = {
3076
+ toolId: created.id,
3077
+ parameters
3078
+ };
3079
+ const execution = await client.tools.execute(created.id, executeRequest);
3080
+ const parsedResult = provider === "daytona" ? parseDaytonaExecutionResult(execution.result) : execution.result;
3081
+ return {
3082
+ success: execution.status === "success",
3083
+ sandboxProvider: provider,
3084
+ language,
3085
+ result: parsedResult,
3086
+ executionId: execution.executionId,
3087
+ ...execution.errorMessage ? { error: execution.errorMessage } : {}
3088
+ };
3089
+ } catch (error) {
3090
+ const message = error instanceof Error ? error.message : String(error);
3091
+ if (debugMode) {
3092
+ console.log(import_chalk15.default.gray(` [sandbox:${provider}] execution error: ${message}`));
3093
+ }
3094
+ return {
3095
+ success: false,
3096
+ sandboxProvider: provider,
3097
+ language,
3098
+ error: message
3099
+ };
3100
+ } finally {
3101
+ if (tempToolId) {
3102
+ try {
3103
+ await client.tools.delete(tempToolId);
3104
+ } catch {
3105
+ }
3106
+ }
3107
+ }
3108
+ }
3109
+ };
3110
+ }
2815
3111
  async function taskAction(agent, options) {
2816
3112
  const apiKey = await ensureAuth();
2817
3113
  if (!apiKey) return;
@@ -2819,6 +3115,11 @@ async function taskAction(agent, options) {
2819
3115
  apiKey,
2820
3116
  baseUrl: getApiUrl()
2821
3117
  });
3118
+ const parsedSandbox = parseSandboxProvider(options.sandbox);
3119
+ if (options.sandbox && !parsedSandbox) {
3120
+ console.error(import_chalk15.default.red(`Invalid --sandbox value "${options.sandbox}". Use: quickjs or daytona`));
3121
+ process.exit(1);
3122
+ }
2822
3123
  let agentId = agent;
2823
3124
  if (!agent.startsWith("agent_")) {
2824
3125
  const spinner = (0, import_ora10.default)("Looking up agent by name...").start();
@@ -2831,13 +3132,21 @@ async function taskAction(agent, options) {
2831
3132
  agentId = found.id;
2832
3133
  spinner.succeed(`Found agent: ${import_chalk15.default.green(agentId)}`);
2833
3134
  } else {
2834
- spinner.fail(`No agent found with name "${agent}"`);
2835
- console.log(import_chalk15.default.gray(` Create one with: runtype agents create -n "${agent}"`));
2836
- process.exit(1);
3135
+ spinner.text = `Creating agent "${agent}"...`;
3136
+ try {
3137
+ const created = await client.agents.create({ name: agent });
3138
+ agentId = created.id;
3139
+ spinner.succeed(`Created agent: ${import_chalk15.default.green(agentId)}`);
3140
+ } catch (createErr) {
3141
+ spinner.fail(`Failed to create agent "${agent}"`);
3142
+ const errMsg = createErr instanceof Error ? createErr.message : String(createErr);
3143
+ console.error(import_chalk15.default.red(errMsg));
3144
+ process.exit(1);
3145
+ }
2837
3146
  }
2838
3147
  } catch (error) {
2839
- spinner.fail("Failed to look up agent");
2840
- const errMsg = error instanceof Error ? error.message : "Unknown error";
3148
+ spinner.fail("Failed to list agents");
3149
+ const errMsg = error instanceof Error ? error.message : String(error);
2841
3150
  console.error(import_chalk15.default.red(errMsg));
2842
3151
  process.exit(1);
2843
3152
  }
@@ -2881,7 +3190,7 @@ async function taskAction(agent, options) {
2881
3190
  }
2882
3191
  console.log(
2883
3192
  import_chalk15.default.gray(
2884
- ` Resume with: runtype marathon ${agent} -m "${options.message}" --resume${options.name ? ` --name ${options.name}` : ""}`
3193
+ ` Resume with: ${buildResumeCommand(agent, options.message, options, parsedSandbox)}`
2885
3194
  )
2886
3195
  );
2887
3196
  process.exit(0);
@@ -2889,32 +3198,63 @@ async function taskAction(agent, options) {
2889
3198
  process.on("SIGINT", onSigint);
2890
3199
  const remainingSessions = maxSessions - priorSessionCount;
2891
3200
  const remainingCost = maxCost ? maxCost - priorCost : void 0;
3201
+ const sandboxPrompt = parsedSandbox ? createSandboxInstructions(parsedSandbox) : "";
3202
+ const taskMessage = sandboxPrompt ? `${options.message}
3203
+
3204
+ ${sandboxPrompt}` : options.message;
2892
3205
  console.log(import_chalk15.default.cyan(`
2893
3206
  Running task "${taskName}" on ${agentId}`));
2894
3207
  console.log(
2895
3208
  import_chalk15.default.gray(
2896
- ` Sessions: ${priorSessionCount > 0 ? `${priorSessionCount} done, ` : ""}${remainingSessions} remaining${maxCost ? ` | Budget: $${maxCost.toFixed(2)}` : ""}`
3209
+ ` Sessions: ${priorSessionCount > 0 ? `${priorSessionCount} done, ` : ""}${remainingSessions} remaining${maxCost ? ` | Budget: $${maxCost.toFixed(2)}` : ""}${options.model ? ` | Model: ${options.model}` : ""}${parsedSandbox ? ` | Sandbox: ${parsedSandbox}` : ""}`
2897
3210
  )
2898
3211
  );
2899
3212
  console.log(import_chalk15.default.gray(` State: ${filePath}
2900
3213
  `));
2901
3214
  try {
2902
3215
  let currentSession = priorSessionCount;
3216
+ let thinkingSpinner = null;
3217
+ let thinkingChars = 0;
2903
3218
  const streamCallbacks = {
2904
3219
  onAgentStart: () => {
2905
3220
  currentSession++;
2906
3221
  console.log(import_chalk15.default.cyan(`
2907
3222
  \u2500\u2500 Session ${currentSession} \u2500\u2500
2908
3223
  `));
3224
+ thinkingChars = 0;
3225
+ thinkingSpinner = (0, import_ora10.default)({ text: import_chalk15.default.dim("Thinking..."), color: "gray" }).start();
2909
3226
  },
2910
3227
  onTurnDelta: (event) => {
2911
3228
  if (event.contentType === "text") {
3229
+ if (thinkingSpinner) {
3230
+ thinkingSpinner.stop();
3231
+ thinkingSpinner = null;
3232
+ }
2912
3233
  process.stdout.write(event.delta);
2913
- } else if (event.contentType === "thinking" && options.debug) {
2914
- process.stdout.write(import_chalk15.default.dim(event.delta));
3234
+ } else if (event.contentType === "thinking") {
3235
+ thinkingChars += event.delta.length;
3236
+ const approxTokens = Math.round(thinkingChars / 4);
3237
+ const tokenStr = approxTokens.toLocaleString();
3238
+ if (!thinkingSpinner) {
3239
+ thinkingSpinner = (0, import_ora10.default)({
3240
+ text: import_chalk15.default.dim(`Thinking... (~${tokenStr} tokens)`),
3241
+ color: "gray"
3242
+ }).start();
3243
+ } else {
3244
+ thinkingSpinner.text = import_chalk15.default.dim(`Thinking... (~${tokenStr} tokens)`);
3245
+ }
3246
+ if (options.debug) {
3247
+ thinkingSpinner.stop();
3248
+ process.stdout.write(import_chalk15.default.dim(event.delta));
3249
+ thinkingSpinner.start();
3250
+ }
2915
3251
  }
2916
3252
  },
2917
3253
  onToolStart: (event) => {
3254
+ if (thinkingSpinner) {
3255
+ thinkingSpinner.stop();
3256
+ thinkingSpinner = null;
3257
+ }
2918
3258
  if (options.debug) {
2919
3259
  console.log(import_chalk15.default.gray(`
2920
3260
  [tool] ${event.toolName}...`));
@@ -2926,18 +3266,100 @@ Running task "${taskName}" on ${agentId}`));
2926
3266
  console.log(import_chalk15.default.gray(` [tool] ${event.toolName} \u2192 ${status}`));
2927
3267
  }
2928
3268
  },
3269
+ onAgentPaused: (event) => {
3270
+ if (thinkingSpinner) {
3271
+ thinkingSpinner.stop();
3272
+ thinkingSpinner = null;
3273
+ }
3274
+ const rawParams = event.parameters;
3275
+ const paramPreview = typeof rawParams === "string" ? `string(${rawParams.length} chars)` : rawParams && typeof rawParams === "object" ? `object(${Object.keys(rawParams).join(", ")})` : typeof rawParams;
3276
+ console.log(
3277
+ import_chalk15.default.cyan(
3278
+ `
3279
+ [local tool] ${event.toolName} called (params: ${paramPreview})`
3280
+ )
3281
+ );
3282
+ if (options.debug) {
3283
+ console.log(import_chalk15.default.gray(` executionId: ${event.executionId}`));
3284
+ console.log(import_chalk15.default.gray(` raw params: ${JSON.stringify(event.parameters).slice(0, 300)}`));
3285
+ }
3286
+ },
2929
3287
  onError: (event) => {
3288
+ if (thinkingSpinner) {
3289
+ thinkingSpinner.stop();
3290
+ thinkingSpinner = null;
3291
+ }
2930
3292
  console.error(import_chalk15.default.red(`
2931
3293
  Error: ${event.error}`));
2932
3294
  }
2933
3295
  };
3296
+ const defaultLocalTools = {
3297
+ read_file: {
3298
+ description: "Read the contents of a file at the given path",
3299
+ parametersSchema: {
3300
+ type: "object",
3301
+ properties: { path: { type: "string", description: "File path to read" } },
3302
+ required: ["path"]
3303
+ },
3304
+ execute: async (args) => {
3305
+ const filePath2 = String(args.path || "");
3306
+ if (!filePath2) return "Error: path is required";
3307
+ return fs3.readFileSync(filePath2, "utf-8");
3308
+ }
3309
+ },
3310
+ write_file: {
3311
+ description: "Write content to a file, creating directories as needed",
3312
+ parametersSchema: {
3313
+ type: "object",
3314
+ properties: {
3315
+ path: { type: "string", description: "File path to write" },
3316
+ content: { type: "string", description: "Content to write" }
3317
+ },
3318
+ required: ["path", "content"]
3319
+ },
3320
+ execute: async (args) => {
3321
+ const filePath2 = String(args.path || "");
3322
+ if (!filePath2) return "Error: path is required";
3323
+ const content = String(args.content || "");
3324
+ const dir = path4.dirname(filePath2);
3325
+ fs3.mkdirSync(dir, { recursive: true });
3326
+ fs3.writeFileSync(filePath2, content);
3327
+ return "ok";
3328
+ }
3329
+ },
3330
+ list_directory: {
3331
+ description: "List files and directories at the given path",
3332
+ parametersSchema: {
3333
+ type: "object",
3334
+ properties: { path: { type: "string", description: 'Directory path (defaults to ".")' } }
3335
+ },
3336
+ execute: async (args) => {
3337
+ const dirPath = String(args.path || ".");
3338
+ return fs3.readdirSync(dirPath).join("\n");
3339
+ }
3340
+ }
3341
+ };
3342
+ const enabledLocalTools = {};
3343
+ if (!options.noLocalTools) {
3344
+ Object.assign(enabledLocalTools, defaultLocalTools);
3345
+ }
3346
+ if (parsedSandbox) {
3347
+ enabledLocalTools.run_sandbox_code = createSandboxLocalTool(
3348
+ client,
3349
+ parsedSandbox,
3350
+ options.debug
3351
+ );
3352
+ }
3353
+ const localTools = Object.keys(enabledLocalTools).length > 0 ? enabledLocalTools : void 0;
2934
3354
  const result = await client.agents.runTask(agentId, {
2935
- message: options.message,
3355
+ message: taskMessage,
2936
3356
  maxSessions: remainingSessions,
2937
3357
  maxCost: remainingCost,
3358
+ model: options.model,
2938
3359
  debugMode: options.debug,
2939
3360
  stream: true,
2940
3361
  streamCallbacks,
3362
+ localTools,
2941
3363
  trackProgress: options.track ? taskName : void 0,
2942
3364
  onSession: (state) => {
2943
3365
  const adjustedState = {
@@ -2951,7 +3373,7 @@ Running task "${taskName}" on ${agentId}`));
2951
3373
  if (latest) {
2952
3374
  const total = adjustedState.sessionCount;
2953
3375
  const costStr = import_chalk15.default.yellow(`$${adjustedState.totalCost.toFixed(4)}`);
2954
- const reasonColor = latest.stopReason === "complete" ? import_chalk15.default.green : import_chalk15.default.gray;
3376
+ const reasonColor = latest.stopReason === "complete" ? import_chalk15.default.green : latest.stopReason === "error" ? import_chalk15.default.red : import_chalk15.default.gray;
2955
3377
  console.log(
2956
3378
  `
2957
3379
  ${import_chalk15.default.dim(`[${total}/${maxSessions}]`)} ${reasonColor(latest.stopReason)} | total: ${costStr}`
@@ -2988,7 +3410,7 @@ Running task "${taskName}" on ${agentId}`));
2988
3410
  console.log(
2989
3411
  import_chalk15.default.gray(
2990
3412
  `
2991
- Resume: runtype marathon ${agent} -m "${options.message}" --resume${options.name ? ` --name ${options.name}` : ""}`
3413
+ Resume: ${buildResumeCommand(agent, options.message, options, parsedSandbox)}`
2992
3414
  )
2993
3415
  );
2994
3416
  }
@@ -3011,7 +3433,7 @@ Task failed: ${errMsg}`));
3011
3433
  }
3012
3434
  }
3013
3435
  function applyTaskOptions(cmd) {
3014
- return cmd.argument("<agent>", "Agent ID or name").requiredOption("-m, --message <text>", "Task message for the agent").option("--max-sessions <n>", "Maximum sessions", "50").option("--max-cost <n>", "Budget in USD").option("--name <name>", "Task name (used for state file, defaults to agent name)").option("--state-dir <path>", "Directory for state files (default: .runtype/tasks/)").option("--resume", "Resume from existing local state").option("--track", "Sync progress to a Runtype record (visible in dashboard)").option("--debug", "Show debug output from each session").option("--json", "Output final result as JSON").action(taskAction);
3436
+ return cmd.argument("<agent>", "Agent ID or name").requiredOption("-m, --message <text>", "Task message for the agent").option("--max-sessions <n>", "Maximum sessions", "50").option("--max-cost <n>", "Budget in USD").option("--model <modelId>", "Model ID to use (overrides agent config)").option("--name <name>", "Task name (used for state file, defaults to agent name)").option("--state-dir <path>", "Directory for state files (default: .runtype/tasks/)").option("--resume", "Resume from existing local state").option("--track", "Sync progress to a Runtype record (visible in dashboard)").option("--debug", "Show debug output from each session").option("--json", "Output final result as JSON").option("--sandbox <provider>", "Enable sandbox code execution tool (quickjs or daytona)").option("--no-local-tools", "Disable built-in local tool execution (read_file, write_file, list_directory)").action(taskAction);
3015
3437
  }
3016
3438
  var taskCommand = applyTaskOptions(
3017
3439
  new import_commander11.Command("task").description("Run a multi-session agent task")
@@ -4095,10 +4517,12 @@ program.addCommand(flowVersionsCommand);
4095
4517
  program.addCommand(createMarathonCommand());
4096
4518
  program.exitOverride();
4097
4519
  try {
4098
- if (!process.argv.slice(2).length) {
4520
+ const userArgs = process.argv.slice(2);
4521
+ const cleanArgs = userArgs[0] === "--" ? userArgs.slice(1) : userArgs;
4522
+ if (!cleanArgs.length) {
4099
4523
  handleNoCommand();
4100
4524
  } else {
4101
- program.parse(process.argv);
4525
+ program.parse(cleanArgs, { from: "user" });
4102
4526
  }
4103
4527
  } catch (error) {
4104
4528
  const commanderError = error;