@thyme-sh/cli 0.2.0 → 0.3.1
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 +324 -102
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/index.ts
|
|
10
|
+
import { readFileSync } from "fs";
|
|
11
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
4
13
|
import { Command } from "commander";
|
|
5
14
|
|
|
6
15
|
// src/commands/init.ts
|
|
@@ -30,7 +39,7 @@ function step(message) {
|
|
|
30
39
|
clack.log.step(message);
|
|
31
40
|
}
|
|
32
41
|
function log2(message) {
|
|
33
|
-
|
|
42
|
+
clack.log.message(message);
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
// src/commands/init.ts
|
|
@@ -176,7 +185,31 @@ Next steps:
|
|
|
176
185
|
// src/utils/tasks.ts
|
|
177
186
|
import { existsSync as existsSync2 } from "fs";
|
|
178
187
|
import { readdir } from "fs/promises";
|
|
179
|
-
import { join as join2 } from "path";
|
|
188
|
+
import { join as join2, resolve } from "path";
|
|
189
|
+
var VALID_TASK_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
190
|
+
var MAX_TASK_NAME_LENGTH = 64;
|
|
191
|
+
function validateTaskName(taskName) {
|
|
192
|
+
if (!taskName) {
|
|
193
|
+
throw new Error("Task name is required");
|
|
194
|
+
}
|
|
195
|
+
if (taskName.length > MAX_TASK_NAME_LENGTH) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Task name too long: ${taskName.length} characters (max: ${MAX_TASK_NAME_LENGTH})`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
if (taskName.includes("..") || taskName.includes("/") || taskName.includes("\\")) {
|
|
201
|
+
throw new Error("Invalid task name: path traversal characters not allowed");
|
|
202
|
+
}
|
|
203
|
+
if (!VALID_TASK_NAME_PATTERN.test(taskName)) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
"Task name must be lowercase alphanumeric with hyphens only"
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
const reservedNames = ["node_modules", "dist", "build", "src", "lib"];
|
|
209
|
+
if (reservedNames.includes(taskName)) {
|
|
210
|
+
throw new Error(`Task name "${taskName}" is reserved`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
180
213
|
async function discoverTasks(projectRoot) {
|
|
181
214
|
const functionsDir = join2(projectRoot, "functions");
|
|
182
215
|
if (!existsSync2(functionsDir)) {
|
|
@@ -184,20 +217,56 @@ async function discoverTasks(projectRoot) {
|
|
|
184
217
|
}
|
|
185
218
|
try {
|
|
186
219
|
const entries = await readdir(functionsDir, { withFileTypes: true });
|
|
187
|
-
return entries.filter((e) => e.isDirectory()).filter((e) =>
|
|
220
|
+
return entries.filter((e) => e.isDirectory()).filter((e) => {
|
|
221
|
+
try {
|
|
222
|
+
validateTaskName(e.name);
|
|
223
|
+
return existsSync2(join2(functionsDir, e.name, "index.ts"));
|
|
224
|
+
} catch {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}).map((e) => e.name);
|
|
188
228
|
} catch {
|
|
189
229
|
return [];
|
|
190
230
|
}
|
|
191
231
|
}
|
|
192
232
|
function getTaskPath(projectRoot, taskName) {
|
|
193
|
-
|
|
233
|
+
validateTaskName(taskName);
|
|
234
|
+
const functionsDir = resolve(projectRoot, "functions");
|
|
235
|
+
const taskPath = resolve(functionsDir, taskName, "index.ts");
|
|
236
|
+
if (!taskPath.startsWith(functionsDir)) {
|
|
237
|
+
throw new Error("Invalid task path: path traversal detected");
|
|
238
|
+
}
|
|
239
|
+
return taskPath;
|
|
194
240
|
}
|
|
195
241
|
function getTaskArgsPath(projectRoot, taskName) {
|
|
196
|
-
|
|
242
|
+
validateTaskName(taskName);
|
|
243
|
+
const functionsDir = resolve(projectRoot, "functions");
|
|
244
|
+
const argsPath = resolve(functionsDir, taskName, "args.json");
|
|
245
|
+
if (!argsPath.startsWith(functionsDir)) {
|
|
246
|
+
throw new Error("Invalid task path: path traversal detected");
|
|
247
|
+
}
|
|
248
|
+
return argsPath;
|
|
197
249
|
}
|
|
198
250
|
function isThymeProject(projectRoot) {
|
|
199
251
|
const functionsDir = join2(projectRoot, "functions");
|
|
200
|
-
|
|
252
|
+
const packageJsonPath = join2(projectRoot, "package.json");
|
|
253
|
+
if (!existsSync2(functionsDir)) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
if (existsSync2(packageJsonPath)) {
|
|
257
|
+
try {
|
|
258
|
+
const fs = __require("fs");
|
|
259
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
260
|
+
const deps = {
|
|
261
|
+
...packageJson.dependencies,
|
|
262
|
+
...packageJson.devDependencies
|
|
263
|
+
};
|
|
264
|
+
return "@thyme-sh/sdk" in deps || "@thyme-sh/cli" in deps;
|
|
265
|
+
} catch {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return true;
|
|
201
270
|
}
|
|
202
271
|
|
|
203
272
|
// src/commands/list.ts
|
|
@@ -215,7 +284,7 @@ async function listCommand() {
|
|
|
215
284
|
}
|
|
216
285
|
step(`Found ${tasks.length} task(s):`);
|
|
217
286
|
for (const task of tasks) {
|
|
218
|
-
|
|
287
|
+
clack.log.message(` ${pc.cyan("\u25CF")} ${task}`);
|
|
219
288
|
}
|
|
220
289
|
outro2("");
|
|
221
290
|
}
|
|
@@ -224,6 +293,7 @@ async function listCommand() {
|
|
|
224
293
|
import { existsSync as existsSync4 } from "fs";
|
|
225
294
|
import { appendFile, readFile, writeFile as writeFile2 } from "fs/promises";
|
|
226
295
|
import { join as join4 } from "path";
|
|
296
|
+
import { z } from "zod";
|
|
227
297
|
|
|
228
298
|
// src/utils/env.ts
|
|
229
299
|
import { existsSync as existsSync3 } from "fs";
|
|
@@ -240,6 +310,20 @@ function getEnv(key, fallback) {
|
|
|
240
310
|
}
|
|
241
311
|
|
|
242
312
|
// src/commands/login.ts
|
|
313
|
+
var verifyResponseSchema = z.object({
|
|
314
|
+
user: z.object({
|
|
315
|
+
id: z.string(),
|
|
316
|
+
name: z.string().optional().default(""),
|
|
317
|
+
email: z.string()
|
|
318
|
+
}),
|
|
319
|
+
organizations: z.array(
|
|
320
|
+
z.object({
|
|
321
|
+
id: z.string(),
|
|
322
|
+
name: z.string(),
|
|
323
|
+
role: z.string()
|
|
324
|
+
})
|
|
325
|
+
).optional().default([])
|
|
326
|
+
});
|
|
243
327
|
async function loginCommand() {
|
|
244
328
|
intro2("Thyme CLI - Login");
|
|
245
329
|
const projectRoot = process.cwd();
|
|
@@ -286,7 +370,14 @@ async function loginCommand() {
|
|
|
286
370
|
error(`Invalid token: ${errorText}`);
|
|
287
371
|
process.exit(1);
|
|
288
372
|
}
|
|
289
|
-
const
|
|
373
|
+
const rawData = await verifyResponse.json();
|
|
374
|
+
const parseResult = verifyResponseSchema.safeParse(rawData);
|
|
375
|
+
if (!parseResult.success) {
|
|
376
|
+
spinner.stop("Invalid API response");
|
|
377
|
+
error(`API returned unexpected data format: ${parseResult.error.message}`);
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
const verifyData = parseResult.data;
|
|
290
381
|
spinner.stop("Token verified!");
|
|
291
382
|
const saveSpinner = clack.spinner();
|
|
292
383
|
saveSpinner.start("Saving token...");
|
|
@@ -338,14 +429,23 @@ async function newCommand(taskName) {
|
|
|
338
429
|
process.exit(1);
|
|
339
430
|
}
|
|
340
431
|
let finalTaskName = taskName;
|
|
341
|
-
if (
|
|
432
|
+
if (finalTaskName) {
|
|
433
|
+
try {
|
|
434
|
+
validateTaskName(finalTaskName);
|
|
435
|
+
} catch (err) {
|
|
436
|
+
error(err instanceof Error ? err.message : String(err));
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
342
440
|
const name = await clack.text({
|
|
343
441
|
message: "What is your task name?",
|
|
344
442
|
placeholder: "my-task",
|
|
345
443
|
validate: (value) => {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
444
|
+
try {
|
|
445
|
+
validateTaskName(value);
|
|
446
|
+
} catch (err) {
|
|
447
|
+
return err instanceof Error ? err.message : String(err);
|
|
448
|
+
}
|
|
349
449
|
}
|
|
350
450
|
});
|
|
351
451
|
if (clack.isCancel(name)) {
|
|
@@ -373,13 +473,19 @@ export default defineTask({
|
|
|
373
473
|
|
|
374
474
|
async run(ctx) {
|
|
375
475
|
const { targetAddress } = ctx.args
|
|
476
|
+
const { logger } = ctx
|
|
376
477
|
|
|
377
|
-
//
|
|
378
|
-
|
|
478
|
+
// Use the logger to output messages to the Thyme dashboard
|
|
479
|
+
logger.info('Starting task execution')
|
|
480
|
+
logger.info(\`Processing address: \${targetAddress}\`)
|
|
379
481
|
|
|
380
482
|
// Example: Read from blockchain using the public client
|
|
381
483
|
// const balance = await ctx.client.getBalance({ address: targetAddress })
|
|
484
|
+
// logger.info(\`Balance: \${balance}\`)
|
|
485
|
+
//
|
|
382
486
|
// const blockNumber = await ctx.client.getBlockNumber()
|
|
487
|
+
// logger.info(\`Current block: \${blockNumber}\`)
|
|
488
|
+
//
|
|
383
489
|
// const value = await ctx.client.readContract({
|
|
384
490
|
// address: targetAddress,
|
|
385
491
|
// abi: [...],
|
|
@@ -387,7 +493,12 @@ export default defineTask({
|
|
|
387
493
|
// args: [someAddress],
|
|
388
494
|
// })
|
|
389
495
|
|
|
496
|
+
// Example: Log warnings and errors
|
|
497
|
+
// logger.warn('Low balance detected')
|
|
498
|
+
// logger.error('Failed to fetch data')
|
|
499
|
+
|
|
390
500
|
// Example: Return calls to execute
|
|
501
|
+
logger.info('Task completed successfully')
|
|
391
502
|
return {
|
|
392
503
|
canExec: true,
|
|
393
504
|
calls: [
|
|
@@ -446,14 +557,37 @@ Next steps:
|
|
|
446
557
|
// src/commands/run.ts
|
|
447
558
|
import { existsSync as existsSync6 } from "fs";
|
|
448
559
|
import { readFile as readFile2 } from "fs/promises";
|
|
449
|
-
import {
|
|
560
|
+
import { createPublicClient, formatEther, http, isAddress } from "viem";
|
|
450
561
|
|
|
451
562
|
// src/deno/runner.ts
|
|
452
563
|
import { spawn } from "child_process";
|
|
453
|
-
import { dirname, resolve } from "path";
|
|
564
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
565
|
+
function escapeJsString(str) {
|
|
566
|
+
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\0/g, "\\0");
|
|
567
|
+
}
|
|
568
|
+
function sanitizeArgs(args) {
|
|
569
|
+
if (args === null || args === void 0) return args;
|
|
570
|
+
if (typeof args !== "object") return args;
|
|
571
|
+
try {
|
|
572
|
+
return JSON.parse(JSON.stringify(args));
|
|
573
|
+
} catch {
|
|
574
|
+
return {};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function sanitizeErrorMessage(error2) {
|
|
578
|
+
let sanitized = error2.replace(/\/[^\s:]+\//g, ".../");
|
|
579
|
+
sanitized = sanitized.replace(/\s+at\s+.+/g, "");
|
|
580
|
+
if (sanitized.length > 500) {
|
|
581
|
+
sanitized = `${sanitized.substring(0, 500)}...`;
|
|
582
|
+
}
|
|
583
|
+
return sanitized.trim();
|
|
584
|
+
}
|
|
454
585
|
async function runInDeno(taskPath, args, config2) {
|
|
455
|
-
const taskDir = dirname(
|
|
456
|
-
const absoluteTaskPath =
|
|
586
|
+
const taskDir = dirname(resolve2(taskPath));
|
|
587
|
+
const absoluteTaskPath = resolve2(taskPath);
|
|
588
|
+
const safeTaskPath = escapeJsString(absoluteTaskPath);
|
|
589
|
+
const safeArgs = sanitizeArgs(args);
|
|
590
|
+
const safeRpcUrl = config2.rpcUrl ? JSON.stringify(config2.rpcUrl) : "undefined";
|
|
457
591
|
const denoFlags = ["run", "--no-prompt"];
|
|
458
592
|
denoFlags.push(`--allow-read=${taskDir}`);
|
|
459
593
|
if (config2.memory) {
|
|
@@ -464,9 +598,25 @@ async function runInDeno(taskPath, args, config2) {
|
|
|
464
598
|
}
|
|
465
599
|
denoFlags.push("-");
|
|
466
600
|
const execScript = `
|
|
467
|
-
import task from '${
|
|
601
|
+
import task from '${safeTaskPath}';
|
|
468
602
|
import { createPublicClient, http } from 'npm:viem@2.21.54';
|
|
469
603
|
|
|
604
|
+
// Logger for local development - prints directly to console
|
|
605
|
+
// (In production, the logger outputs with __THYME_LOG__ prefix for capture)
|
|
606
|
+
class Logger {
|
|
607
|
+
info(message) {
|
|
608
|
+
console.log('[INFO]', message);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
warn(message) {
|
|
612
|
+
console.log('[WARN]', message);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
error(message) {
|
|
616
|
+
console.log('[ERROR]', message);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
470
620
|
// Create RPC request counter
|
|
471
621
|
let rpcRequestCount = 0;
|
|
472
622
|
|
|
@@ -487,12 +637,13 @@ const countingHttp = (url) => {
|
|
|
487
637
|
|
|
488
638
|
// Create public client for blockchain reads
|
|
489
639
|
const client = createPublicClient({
|
|
490
|
-
transport: countingHttp(${
|
|
640
|
+
transport: countingHttp(${safeRpcUrl}),
|
|
491
641
|
});
|
|
492
642
|
|
|
493
643
|
const context = {
|
|
494
|
-
args: ${JSON.stringify(
|
|
644
|
+
args: ${JSON.stringify(safeArgs)},
|
|
495
645
|
client,
|
|
646
|
+
logger: new Logger(),
|
|
496
647
|
};
|
|
497
648
|
|
|
498
649
|
try {
|
|
@@ -506,7 +657,8 @@ try {
|
|
|
506
657
|
const endMemory = Deno.memoryUsage().heapUsed;
|
|
507
658
|
|
|
508
659
|
const executionTime = endTime - startTime;
|
|
509
|
-
|
|
660
|
+
// Ensure memory measurement is non-negative (GC can cause negative values)
|
|
661
|
+
const memoryUsed = Math.max(0, endMemory - startMemory);
|
|
510
662
|
|
|
511
663
|
console.log('__THYME_RESULT__' + JSON.stringify(result));
|
|
512
664
|
console.log('__THYME_STATS__' + JSON.stringify({ executionTime, memoryUsed, rpcRequestCount }));
|
|
@@ -515,10 +667,9 @@ try {
|
|
|
515
667
|
Deno.exit(1);
|
|
516
668
|
}
|
|
517
669
|
`;
|
|
518
|
-
return new Promise((
|
|
670
|
+
return new Promise((resolve3) => {
|
|
519
671
|
const proc = spawn("deno", denoFlags, {
|
|
520
672
|
stdio: ["pipe", "pipe", "pipe"],
|
|
521
|
-
timeout: config2.timeout * 1e3,
|
|
522
673
|
cwd: taskDir
|
|
523
674
|
});
|
|
524
675
|
let stdout = "";
|
|
@@ -534,10 +685,12 @@ try {
|
|
|
534
685
|
});
|
|
535
686
|
proc.on("close", (code) => {
|
|
536
687
|
if (code !== 0) {
|
|
537
|
-
|
|
688
|
+
resolve3({
|
|
538
689
|
success: false,
|
|
539
690
|
logs,
|
|
540
|
-
error:
|
|
691
|
+
error: sanitizeErrorMessage(
|
|
692
|
+
stderr || `Process exited with code ${code}`
|
|
693
|
+
)
|
|
541
694
|
});
|
|
542
695
|
return;
|
|
543
696
|
}
|
|
@@ -563,7 +716,7 @@ try {
|
|
|
563
716
|
memoryUsed: void 0,
|
|
564
717
|
rpcRequestCount: void 0
|
|
565
718
|
};
|
|
566
|
-
|
|
719
|
+
resolve3({
|
|
567
720
|
success: true,
|
|
568
721
|
result,
|
|
569
722
|
logs,
|
|
@@ -572,27 +725,29 @@ try {
|
|
|
572
725
|
rpcRequestCount: stats.rpcRequestCount
|
|
573
726
|
});
|
|
574
727
|
} catch (error2) {
|
|
575
|
-
|
|
728
|
+
resolve3({
|
|
576
729
|
success: false,
|
|
577
730
|
logs,
|
|
578
|
-
error:
|
|
731
|
+
error: sanitizeErrorMessage(
|
|
732
|
+
`Failed to parse result: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
733
|
+
)
|
|
579
734
|
});
|
|
580
735
|
}
|
|
581
736
|
});
|
|
582
737
|
proc.on("error", (error2) => {
|
|
583
|
-
|
|
738
|
+
resolve3({
|
|
584
739
|
success: false,
|
|
585
740
|
logs,
|
|
586
|
-
error: `Failed to spawn Deno: ${error2.message}`
|
|
741
|
+
error: sanitizeErrorMessage(`Failed to spawn Deno: ${error2.message}`)
|
|
587
742
|
});
|
|
588
743
|
});
|
|
589
744
|
});
|
|
590
745
|
}
|
|
591
746
|
async function checkDeno() {
|
|
592
|
-
return new Promise((
|
|
747
|
+
return new Promise((resolve3) => {
|
|
593
748
|
const proc = spawn("deno", ["--version"], { stdio: "ignore" });
|
|
594
|
-
proc.on("close", (code) =>
|
|
595
|
-
proc.on("error", () =>
|
|
749
|
+
proc.on("close", (code) => resolve3(code === 0));
|
|
750
|
+
proc.on("error", () => resolve3(false));
|
|
596
751
|
});
|
|
597
752
|
}
|
|
598
753
|
|
|
@@ -611,7 +766,14 @@ async function runCommand(taskName, options = {}) {
|
|
|
611
766
|
process.exit(1);
|
|
612
767
|
}
|
|
613
768
|
let finalTaskName = taskName;
|
|
614
|
-
if (
|
|
769
|
+
if (finalTaskName) {
|
|
770
|
+
try {
|
|
771
|
+
validateTaskName(finalTaskName);
|
|
772
|
+
} catch (err) {
|
|
773
|
+
error(err instanceof Error ? err.message : String(err));
|
|
774
|
+
process.exit(1);
|
|
775
|
+
}
|
|
776
|
+
} else {
|
|
615
777
|
const tasks = await discoverTasks(projectRoot);
|
|
616
778
|
if (tasks.length === 0) {
|
|
617
779
|
error("No tasks found. Create one with `thyme new`");
|
|
@@ -627,8 +789,15 @@ async function runCommand(taskName, options = {}) {
|
|
|
627
789
|
}
|
|
628
790
|
finalTaskName = selected;
|
|
629
791
|
}
|
|
630
|
-
|
|
631
|
-
|
|
792
|
+
let taskPath;
|
|
793
|
+
let argsPath;
|
|
794
|
+
try {
|
|
795
|
+
taskPath = getTaskPath(projectRoot, finalTaskName);
|
|
796
|
+
argsPath = getTaskArgsPath(projectRoot, finalTaskName);
|
|
797
|
+
} catch (err) {
|
|
798
|
+
error(err instanceof Error ? err.message : String(err));
|
|
799
|
+
process.exit(1);
|
|
800
|
+
}
|
|
632
801
|
if (!existsSync6(taskPath)) {
|
|
633
802
|
error(`Task "${finalTaskName}" not found`);
|
|
634
803
|
process.exit(1);
|
|
@@ -685,7 +854,7 @@ async function runCommand(taskName, options = {}) {
|
|
|
685
854
|
step("Calls to execute:");
|
|
686
855
|
for (const call of result.result.calls) {
|
|
687
856
|
log2(` ${pc.cyan("\u2192")} to: ${call.to}`);
|
|
688
|
-
log2(` data: ${call.data
|
|
857
|
+
log2(` data: ${call.data}`);
|
|
689
858
|
}
|
|
690
859
|
if (options.simulate) {
|
|
691
860
|
log2("");
|
|
@@ -739,30 +908,53 @@ async function simulateCalls(calls) {
|
|
|
739
908
|
info(`Chain ID: ${chainId}`);
|
|
740
909
|
info(`Block: ${blockNumber}`);
|
|
741
910
|
info(`Account: ${account}`);
|
|
911
|
+
if (!isAddress(account)) {
|
|
912
|
+
spinner.stop("Invalid account address");
|
|
913
|
+
log2("");
|
|
914
|
+
error(`SIMULATE_ACCOUNT is not a valid Ethereum address: ${account}`);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
742
917
|
const simulationSpinner = clack.spinner();
|
|
743
918
|
simulationSpinner.start("Running simulation...");
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
919
|
+
const { results } = await client.simulateCalls({
|
|
920
|
+
account,
|
|
921
|
+
calls: calls.map((call) => ({
|
|
922
|
+
to: call.to,
|
|
923
|
+
data: call.data
|
|
924
|
+
}))
|
|
925
|
+
});
|
|
926
|
+
simulationSpinner.stop("Simulation complete");
|
|
927
|
+
const failedCalls = [];
|
|
928
|
+
for (let i = 0; i < results.length; i++) {
|
|
929
|
+
const result = results[i];
|
|
930
|
+
const call = calls[i];
|
|
931
|
+
if (!result || !call) continue;
|
|
932
|
+
if (result.status === "failure") {
|
|
933
|
+
failedCalls.push({
|
|
934
|
+
index: i,
|
|
935
|
+
call,
|
|
936
|
+
error: result.error?.message || "Unknown error"
|
|
750
937
|
});
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (failedCalls.length > 0) {
|
|
941
|
+
log2("");
|
|
942
|
+
error("Some calls would revert:");
|
|
943
|
+
for (const failed of failedCalls) {
|
|
754
944
|
error(
|
|
755
|
-
`Call to ${call.to}
|
|
945
|
+
` Call ${failed.index + 1} to ${failed.call.to}: ${failed.error}`
|
|
756
946
|
);
|
|
757
|
-
return;
|
|
758
947
|
}
|
|
948
|
+
return;
|
|
759
949
|
}
|
|
760
950
|
const gasPrice = await client.getGasPrice();
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
951
|
+
clack.log.step("Simulation results:");
|
|
952
|
+
clack.log.success("All calls would succeed");
|
|
953
|
+
const totalGas = results.reduce((sum, r) => sum + (r.gasUsed || 0n), 0n);
|
|
954
|
+
if (totalGas > 0n) {
|
|
955
|
+
clack.log.message(` Total gas: ${totalGas.toString()}`);
|
|
956
|
+
}
|
|
957
|
+
clack.log.message(` Gas price: ${formatEther(gasPrice)} ETH`);
|
|
766
958
|
} catch (err) {
|
|
767
959
|
spinner.stop("Simulation failed");
|
|
768
960
|
log2("");
|
|
@@ -772,6 +964,7 @@ async function simulateCalls(calls) {
|
|
|
772
964
|
|
|
773
965
|
// src/commands/upload.ts
|
|
774
966
|
import { existsSync as existsSync7 } from "fs";
|
|
967
|
+
import { z as z2 } from "zod";
|
|
775
968
|
|
|
776
969
|
// src/utils/bundler.ts
|
|
777
970
|
import { readFile as readFile3 } from "fs/promises";
|
|
@@ -857,7 +1050,11 @@ async function bundleTask(taskPath) {
|
|
|
857
1050
|
if (result.outputFiles.length === 0) {
|
|
858
1051
|
throw new Error("No output from bundler");
|
|
859
1052
|
}
|
|
860
|
-
const
|
|
1053
|
+
const outputFile = result.outputFiles[0];
|
|
1054
|
+
if (!outputFile) {
|
|
1055
|
+
throw new Error("No output from bundler");
|
|
1056
|
+
}
|
|
1057
|
+
const bundle = outputFile.text;
|
|
861
1058
|
return {
|
|
862
1059
|
source,
|
|
863
1060
|
bundle
|
|
@@ -877,50 +1074,13 @@ function compressTask(source, bundle) {
|
|
|
877
1074
|
// src/utils/schema-extractor.ts
|
|
878
1075
|
function extractSchemaFromTask(taskCode) {
|
|
879
1076
|
try {
|
|
880
|
-
const schemaExtractor = `
|
|
881
|
-
const { z } = require('zod');
|
|
882
|
-
const { zodToJsonSchema } = require('zod-to-json-schema');
|
|
883
|
-
|
|
884
|
-
// Extended z with address validator
|
|
885
|
-
const zodExtended = {
|
|
886
|
-
...z,
|
|
887
|
-
address: () => z.string().refine((val) => /^0x[a-fA-F0-9]{40}$/.test(val), {
|
|
888
|
-
message: 'Invalid Ethereum address',
|
|
889
|
-
}),
|
|
890
|
-
};
|
|
891
|
-
|
|
892
|
-
// Mock defineTask to capture schema
|
|
893
|
-
let capturedSchema = null;
|
|
894
|
-
const defineTask = (definition) => {
|
|
895
|
-
if (definition.schema) {
|
|
896
|
-
capturedSchema = definition.schema;
|
|
897
|
-
}
|
|
898
|
-
return definition;
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
// Mock viem exports
|
|
902
|
-
const encodeFunctionData = () => '0x';
|
|
903
|
-
|
|
904
|
-
// Evaluate task code
|
|
905
|
-
${taskCode}
|
|
906
|
-
|
|
907
|
-
// Convert schema to JSON Schema
|
|
908
|
-
if (capturedSchema) {
|
|
909
|
-
const jsonSchema = zodToJsonSchema(capturedSchema, { target: 'openApi3' });
|
|
910
|
-
console.log(JSON.stringify(jsonSchema));
|
|
911
|
-
} else {
|
|
912
|
-
console.log('null');
|
|
913
|
-
}
|
|
914
|
-
`;
|
|
915
1077
|
const schemaMatch = taskCode.match(/schema:\s*z\.object\(\{([^}]+)\}\)/);
|
|
916
|
-
if (!schemaMatch) {
|
|
1078
|
+
if (!schemaMatch || !schemaMatch[1]) {
|
|
917
1079
|
return null;
|
|
918
1080
|
}
|
|
919
1081
|
const schemaContent = schemaMatch[1];
|
|
920
1082
|
const fields = {};
|
|
921
|
-
const fieldMatches = schemaContent.matchAll(
|
|
922
|
-
/(\w+):\s*z\.(\w+)\(\)/g
|
|
923
|
-
);
|
|
1083
|
+
const fieldMatches = schemaContent.matchAll(/(\w+):\s*z\.(\w+)\(\)/g);
|
|
924
1084
|
for (const match of fieldMatches) {
|
|
925
1085
|
const [, fieldName, fieldType] = match;
|
|
926
1086
|
if (fieldName && fieldType) {
|
|
@@ -966,6 +1126,22 @@ function extractSchemaFromTask(taskCode) {
|
|
|
966
1126
|
}
|
|
967
1127
|
|
|
968
1128
|
// src/commands/upload.ts
|
|
1129
|
+
var organizationSchema = z2.object({
|
|
1130
|
+
id: z2.string(),
|
|
1131
|
+
name: z2.string(),
|
|
1132
|
+
role: z2.string()
|
|
1133
|
+
});
|
|
1134
|
+
var verifyResponseSchema2 = z2.object({
|
|
1135
|
+
user: z2.object({
|
|
1136
|
+
id: z2.string(),
|
|
1137
|
+
name: z2.string().optional().default(""),
|
|
1138
|
+
email: z2.string()
|
|
1139
|
+
}),
|
|
1140
|
+
organizations: z2.array(organizationSchema).optional().default([])
|
|
1141
|
+
});
|
|
1142
|
+
var uploadResponseSchema = z2.object({
|
|
1143
|
+
taskId: z2.string().optional()
|
|
1144
|
+
});
|
|
969
1145
|
async function uploadCommand(taskName, organizationId) {
|
|
970
1146
|
intro2("Thyme CLI - Upload Task");
|
|
971
1147
|
const projectRoot = process.cwd();
|
|
@@ -987,7 +1163,14 @@ async function uploadCommand(taskName, organizationId) {
|
|
|
987
1163
|
process.exit(1);
|
|
988
1164
|
}
|
|
989
1165
|
let finalTaskName = taskName;
|
|
990
|
-
if (
|
|
1166
|
+
if (finalTaskName) {
|
|
1167
|
+
try {
|
|
1168
|
+
validateTaskName(finalTaskName);
|
|
1169
|
+
} catch (err) {
|
|
1170
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1171
|
+
process.exit(1);
|
|
1172
|
+
}
|
|
1173
|
+
} else {
|
|
991
1174
|
const tasks = await discoverTasks(projectRoot);
|
|
992
1175
|
if (tasks.length === 0) {
|
|
993
1176
|
error("No tasks found. Create one with `thyme new`");
|
|
@@ -1018,8 +1201,14 @@ async function uploadCommand(taskName, organizationId) {
|
|
|
1018
1201
|
error("Failed to authenticate. Please run `thyme login` again.");
|
|
1019
1202
|
process.exit(1);
|
|
1020
1203
|
}
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1204
|
+
const rawData = await verifyResponse.json();
|
|
1205
|
+
const parseResult = verifyResponseSchema2.safeParse(rawData);
|
|
1206
|
+
if (!parseResult.success) {
|
|
1207
|
+
orgSpinner.stop("Invalid API response");
|
|
1208
|
+
error(`API returned unexpected data format: ${parseResult.error.message}`);
|
|
1209
|
+
process.exit(1);
|
|
1210
|
+
}
|
|
1211
|
+
organizations = parseResult.data.organizations;
|
|
1023
1212
|
orgSpinner.stop("Organizations loaded");
|
|
1024
1213
|
} catch (err) {
|
|
1025
1214
|
orgSpinner.stop("Failed to fetch organizations");
|
|
@@ -1027,14 +1216,18 @@ async function uploadCommand(taskName, organizationId) {
|
|
|
1027
1216
|
process.exit(1);
|
|
1028
1217
|
}
|
|
1029
1218
|
if (organizations.length === 0) {
|
|
1030
|
-
error(
|
|
1219
|
+
error(
|
|
1220
|
+
"You are not a member of any organizations. Please create or join an organization first."
|
|
1221
|
+
);
|
|
1031
1222
|
process.exit(1);
|
|
1032
1223
|
}
|
|
1033
1224
|
let selectedOrgId = organizationId;
|
|
1034
1225
|
if (selectedOrgId) {
|
|
1035
1226
|
const orgExists = organizations.find((org) => org.id === selectedOrgId);
|
|
1036
1227
|
if (!orgExists) {
|
|
1037
|
-
error(
|
|
1228
|
+
error(
|
|
1229
|
+
`Organization with ID "${selectedOrgId}" not found or you don't have access to it.`
|
|
1230
|
+
);
|
|
1038
1231
|
process.exit(1);
|
|
1039
1232
|
}
|
|
1040
1233
|
} else {
|
|
@@ -1051,7 +1244,13 @@ async function uploadCommand(taskName, organizationId) {
|
|
|
1051
1244
|
}
|
|
1052
1245
|
selectedOrgId = selected;
|
|
1053
1246
|
}
|
|
1054
|
-
|
|
1247
|
+
let taskPath;
|
|
1248
|
+
try {
|
|
1249
|
+
taskPath = getTaskPath(projectRoot, finalTaskName);
|
|
1250
|
+
} catch (err) {
|
|
1251
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1252
|
+
process.exit(1);
|
|
1253
|
+
}
|
|
1055
1254
|
if (!existsSync7(taskPath)) {
|
|
1056
1255
|
error(`Task "${finalTaskName}" not found`);
|
|
1057
1256
|
process.exit(1);
|
|
@@ -1086,7 +1285,14 @@ async function uploadCommand(taskName, organizationId) {
|
|
|
1086
1285
|
const errorText = await response.text();
|
|
1087
1286
|
throw new Error(`Upload failed: ${errorText}`);
|
|
1088
1287
|
}
|
|
1089
|
-
const
|
|
1288
|
+
const rawResult = await response.json();
|
|
1289
|
+
const resultParseResult = uploadResponseSchema.safeParse(rawResult);
|
|
1290
|
+
if (!resultParseResult.success) {
|
|
1291
|
+
throw new Error(
|
|
1292
|
+
`Invalid upload response: ${resultParseResult.error.message}`
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
const result = resultParseResult.data;
|
|
1090
1296
|
spinner.stop("Task uploaded successfully!");
|
|
1091
1297
|
const selectedOrg = organizations.find((org) => org.id === selectedOrgId);
|
|
1092
1298
|
clack.log.message("");
|
|
@@ -1115,8 +1321,24 @@ Configure triggers in the dashboard: ${pc.cyan("https://thyme.sh/dashboard")}`
|
|
|
1115
1321
|
}
|
|
1116
1322
|
|
|
1117
1323
|
// src/index.ts
|
|
1324
|
+
var __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
1325
|
+
var version = "0.0.0";
|
|
1326
|
+
try {
|
|
1327
|
+
const packageJson = JSON.parse(
|
|
1328
|
+
readFileSync(join6(__dirname2, "../package.json"), "utf-8")
|
|
1329
|
+
);
|
|
1330
|
+
version = packageJson.version || version;
|
|
1331
|
+
} catch {
|
|
1332
|
+
try {
|
|
1333
|
+
const packageJson = JSON.parse(
|
|
1334
|
+
readFileSync(join6(__dirname2, "../../package.json"), "utf-8")
|
|
1335
|
+
);
|
|
1336
|
+
version = packageJson.version || version;
|
|
1337
|
+
} catch {
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1118
1340
|
var program = new Command();
|
|
1119
|
-
program.name("thyme").description("CLI for developing and deploying Thyme tasks").version(
|
|
1341
|
+
program.name("thyme").description("CLI for developing and deploying Thyme tasks").version(version);
|
|
1120
1342
|
program.command("init").description("Initialize a new Thyme project").argument("[name]", "Project name").action(initCommand);
|
|
1121
1343
|
program.command("new").description("Create a new task").argument("[name]", "Task name").action(newCommand);
|
|
1122
1344
|
program.command("run").description("Run a task locally").argument("[task]", "Task name").option("--simulate", "Simulate on-chain execution").action((task, options) => runCommand(task, options));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/ui.ts","../src/utils/tasks.ts","../src/commands/list.ts","../src/commands/login.ts","../src/utils/env.ts","../src/commands/new.ts","../src/commands/run.ts","../src/deno/runner.ts","../src/commands/upload.ts","../src/utils/bundler.ts","../src/utils/compress.ts","../src/utils/schema-extractor.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { initCommand } from './commands/init'\nimport { listCommand } from './commands/list'\nimport { loginCommand } from './commands/login'\nimport { newCommand } from './commands/new'\nimport { runCommand } from './commands/run'\nimport { uploadCommand } from './commands/upload'\n\nconst program = new Command()\n\nprogram\n\t.name('thyme')\n\t.description('CLI for developing and deploying Thyme tasks')\n\t.version('0.1.0')\n\nprogram\n\t.command('init')\n\t.description('Initialize a new Thyme project')\n\t.argument('[name]', 'Project name')\n\t.action(initCommand)\n\nprogram\n\t.command('new')\n\t.description('Create a new task')\n\t.argument('[name]', 'Task name')\n\t.action(newCommand)\n\nprogram\n\t.command('run')\n\t.description('Run a task locally')\n\t.argument('[task]', 'Task name')\n\t.option('--simulate', 'Simulate on-chain execution')\n\t.action((task, options) => runCommand(task, options))\n\nprogram.command('list').description('List all tasks').action(listCommand)\n\nprogram\n\t.command('login')\n\t.description('Authenticate with Thyme Cloud')\n\t.action(loginCommand)\n\nprogram\n\t.command('upload')\n\t.description('Upload a task to Thyme Cloud')\n\t.argument('[task]', 'Task name')\n\t.option('-o, --organization <id>', 'Organization ID to upload to')\n\t.action((task, options) => uploadCommand(task, options.organization))\n\nprogram.parse()\n","import { existsSync } from 'node:fs'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { clack, error, intro, outro, pc } from '../utils/ui'\n\nexport async function initCommand(projectName?: string) {\n\tintro('Thyme CLI - Initialize Project')\n\n\t// Prompt for project name if not provided\n\tlet finalProjectName = projectName\n\tif (!finalProjectName) {\n\t\tconst name = await clack.text({\n\t\t\tmessage: 'What is your project name?',\n\t\t\tplaceholder: 'my-thyme-project',\n\t\t\tvalidate: (value) => {\n\t\t\t\tif (!value) return 'Project name is required'\n\t\t\t\tif (!/^[a-z0-9-]+$/.test(value))\n\t\t\t\t\treturn 'Project name must be lowercase alphanumeric with hyphens'\n\t\t\t},\n\t\t})\n\n\t\tif (clack.isCancel(name)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tfinalProjectName = name as string\n\t}\n\n\tconst projectPath = join(process.cwd(), finalProjectName)\n\n\t// Check if directory exists\n\tif (existsSync(projectPath)) {\n\t\terror(`Directory \"${finalProjectName}\" already exists`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Creating project structure...')\n\n\ttry {\n\t\t// Create directories\n\t\tawait mkdir(projectPath, { recursive: true })\n\t\tawait mkdir(join(projectPath, 'functions'), { recursive: true })\n\n\t\t// Create package.json\n\t\tconst packageJson = {\n\t\t\tname: finalProjectName,\n\t\t\tversion: '0.1.0',\n\t\t\ttype: 'module',\n\t\t\tprivate: true,\n\t\t\tscripts: {\n\t\t\t\tdev: 'thyme run',\n\t\t\t},\n\t\t\tdependencies: {\n\t\t\t\t'@thyme-sh/sdk': '^0.1.0',\n\t\t\t\tviem: '^2.21.54',\n\t\t\t\tzod: '^3.24.1',\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t'@thyme-sh/cli': '^0.1.0',\n\t\t\t\ttypescript: '^5.7.2',\n\t\t\t},\n\t\t}\n\n\t\tawait writeFile(\n\t\t\tjoin(projectPath, 'package.json'),\n\t\t\tJSON.stringify(packageJson, null, 2),\n\t\t)\n\n\t\t// Create tsconfig.json\n\t\tconst tsconfig = {\n\t\t\tcompilerOptions: {\n\t\t\t\ttarget: 'ES2022',\n\t\t\t\tmodule: 'ESNext',\n\t\t\t\tmoduleResolution: 'bundler',\n\t\t\t\tlib: ['ES2022', 'DOM'],\n\t\t\t\tstrict: true,\n\t\t\t\tesModuleInterop: true,\n\t\t\t\tskipLibCheck: true,\n\t\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\t\tresolveJsonModule: true,\n\t\t\t},\n\t\t\tinclude: ['functions/**/*'],\n\t\t}\n\n\t\tawait writeFile(\n\t\t\tjoin(projectPath, 'tsconfig.json'),\n\t\t\tJSON.stringify(tsconfig, null, 2),\n\t\t)\n\n\t\t// Create .env.example\n\t\tconst envExample = `# Simulation settings (for --simulate flag)\nRPC_URL=https://eth-sepolia.g.alchemy.com/v2/your-key\nSIMULATE_ACCOUNT=0x742d35Cc6634C0532925a3b844Bc454e4438f44e\n\n# Cloud authentication (set by \\`thyme login\\`)\nTHYME_AUTH_TOKEN=\n\n# Cloud API URL (required - your Convex deployment URL)\n# Example: https://your-deployment.convex.cloud\nTHYME_API_URL=\n`\n\n\t\tawait writeFile(join(projectPath, '.env.example'), envExample)\n\n\t\t// Create .gitignore\n\t\tconst gitignore = `node_modules/\ndist/\n.env\n.env.local\n*.log\n`\n\n\t\tawait writeFile(join(projectPath, '.gitignore'), gitignore)\n\n\t\t// Create README\n\t\tconst readme = `# ${finalProjectName}\n\nA Thyme project for Web3 automation tasks.\n\n## Getting Started\n\n\\`\\`\\`bash\n# Install dependencies\nnpm install\n\n# Create a new task\nthyme new my-task\n\n# Run a task locally\nthyme run my-task\n\n# Simulate on-chain\nthyme run my-task --simulate\n\n# Deploy to cloud\nthyme login\nthyme upload my-task\n\\`\\`\\`\n\n## Project Structure\n\n\\`\\`\\`\nfunctions/\n my-task/\n index.ts # Task definition\n args.json # Test arguments\n\\`\\`\\`\n`\n\n\t\tawait writeFile(join(projectPath, 'README.md'), readme)\n\n\t\tspinner.stop('Project created successfully!')\n\n\t\toutro(\n\t\t\t`${pc.green('✓')} Project initialized!\\n\\nNext steps:\\n ${pc.cyan('cd')} ${finalProjectName}\\n ${pc.cyan('npm install')}\\n ${pc.cyan('thyme new')} my-task\\n ${pc.cyan('thyme run')} my-task`,\n\t\t)\n\t} catch (err) {\n\t\tspinner.stop('Failed to create project')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n}\n","import * as clack from '@clack/prompts'\nimport pc from 'picocolors'\n\nexport { clack, pc }\n\nexport function intro(title: string) {\n\tclack.intro(pc.bgCyan(pc.black(` ${title} `)))\n}\n\nexport function outro(message: string) {\n\tclack.outro(message)\n}\n\nexport function success(message: string) {\n\tclack.log.success(pc.green(message))\n}\n\nexport function error(message: string) {\n\tclack.log.error(pc.red(message))\n}\n\nexport function info(message: string) {\n\tclack.log.info(pc.cyan(message))\n}\n\nexport function warn(message: string) {\n\tclack.log.warn(pc.yellow(message))\n}\n\nexport function step(message: string) {\n\tclack.log.step(message)\n}\n\nexport function log(message: string) {\n\tconsole.log(message)\n}\n","import { existsSync } from 'node:fs'\nimport { readdir } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n/**\n * Discover all tasks in the functions directory\n */\nexport async function discoverTasks(projectRoot: string): Promise<string[]> {\n\tconst functionsDir = join(projectRoot, 'functions')\n\n\tif (!existsSync(functionsDir)) {\n\t\treturn []\n\t}\n\n\ttry {\n\t\tconst entries = await readdir(functionsDir, { withFileTypes: true })\n\n\t\treturn entries\n\t\t\t.filter((e) => e.isDirectory())\n\t\t\t.filter((e) => existsSync(join(functionsDir, e.name, 'index.ts')))\n\t\t\t.map((e) => e.name)\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Get the path to a task's index file\n */\nexport function getTaskPath(projectRoot: string, taskName: string): string {\n\treturn join(projectRoot, 'functions', taskName, 'index.ts')\n}\n\n/**\n * Get the path to a task's args file\n */\nexport function getTaskArgsPath(projectRoot: string, taskName: string): string {\n\treturn join(projectRoot, 'functions', taskName, 'args.json')\n}\n\n/**\n * Check if we're in a Thyme project\n */\nexport function isThymeProject(projectRoot: string): boolean {\n\tconst functionsDir = join(projectRoot, 'functions')\n\treturn existsSync(functionsDir)\n}\n","import { discoverTasks, isThymeProject } from '../utils/tasks'\nimport { intro, outro, pc, step } from '../utils/ui'\n\nexport async function listCommand() {\n\tintro('Thyme CLI - List Tasks')\n\n\tconst projectRoot = process.cwd()\n\n\tif (!isThymeProject(projectRoot)) {\n\t\toutro(pc.red('Not in a Thyme project'))\n\t\tprocess.exit(1)\n\t}\n\n\tconst tasks = await discoverTasks(projectRoot)\n\n\tif (tasks.length === 0) {\n\t\toutro(pc.yellow('No tasks found. Create one with `thyme new`'))\n\t\treturn\n\t}\n\n\tstep(`Found ${tasks.length} task(s):`)\n\tfor (const task of tasks) {\n\t\tconsole.log(` ${pc.cyan('●')} ${task}`)\n\t}\n\n\toutro('')\n}\n","import { existsSync } from 'node:fs'\nimport { appendFile, readFile, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { getEnv, loadEnv } from '../utils/env'\nimport { clack, error, info, intro, outro, pc } from '../utils/ui'\n\nexport async function loginCommand() {\n\tintro('Thyme CLI - Login')\n\n\tconst projectRoot = process.cwd()\n\tconst envPath = join(projectRoot, '.env')\n\n\t// Load environment variables\n\tloadEnv(projectRoot)\n\n\t// Show instructions\n\tinfo('To authenticate with Thyme Cloud:')\n\tclack.log.message(\n\t\t` 1. Visit ${pc.cyan('https://thyme.sh/settings/api-keys')}`,\n\t)\n\tclack.log.message(' 2. Generate a new API token')\n\tclack.log.message(' 3. Copy the token and paste it below')\n\tclack.log.message('')\n\n\t// Prompt for token\n\tconst token = await clack.password({\n\t\tmessage: 'Paste your API token:',\n\t\tvalidate: (value) => {\n\t\t\tif (!value) return 'Token is required'\n\t\t\tif (value.length < 10) return 'Token seems too short'\n\t\t},\n\t})\n\n\tif (clack.isCancel(token)) {\n\t\tclack.cancel('Operation cancelled')\n\t\tprocess.exit(0)\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Verifying token...')\n\n\ttry {\n\t\t// Get API URL (Convex deployment URL)\n\t\tconst apiUrl = getEnv('THYME_API_URL')\n\n\t\tif (!apiUrl) {\n\t\t\tspinner.stop('Configuration error')\n\t\t\terror(\n\t\t\t\t'THYME_API_URL is not set. Please set it to your Convex deployment URL (e.g., https://your-deployment.convex.cloud)',\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Verify token with API\n\t\tconst verifyResponse = await fetch(`${apiUrl}/api/auth/verify`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t},\n\t\t})\n\n\t\tif (!verifyResponse.ok) {\n\t\t\tspinner.stop('Token verification failed')\n\t\t\tconst errorText = await verifyResponse.text()\n\t\t\terror(`Invalid token: ${errorText}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst verifyData = (await verifyResponse.json()) as {\n\t\t\tuser: {\n\t\t\t\tid: string\n\t\t\t\tname: string\n\t\t\t\temail: string\n\t\t\t}\n\t\t\torganizations: {\n\t\t\t\tid: string\n\t\t\t\tname: string\n\t\t\t\trole: string\n\t\t\t}[]\n\t\t}\n\n\t\tspinner.stop('Token verified!')\n\n\t\t// Save token\n\t\tconst saveSpinner = clack.spinner()\n\t\tsaveSpinner.start('Saving token...')\n\n\t\t// Read existing .env or create new\n\t\tlet envContent = ''\n\t\tif (existsSync(envPath)) {\n\t\t\tenvContent = await readFile(envPath, 'utf-8')\n\t\t}\n\n\t\t// Check if THYME_AUTH_TOKEN already exists\n\t\tconst tokenRegex = /^THYME_AUTH_TOKEN=.*$/m\n\t\tif (tokenRegex.test(envContent)) {\n\t\t\t// Replace existing token\n\t\t\tenvContent = envContent.replace(tokenRegex, `THYME_AUTH_TOKEN=${token}`)\n\t\t\tawait writeFile(envPath, envContent)\n\t\t} else {\n\t\t\t// Append new token\n\t\t\tconst newLine = envContent && !envContent.endsWith('\\n') ? '\\n' : ''\n\t\t\tawait appendFile(envPath, `${newLine}THYME_AUTH_TOKEN=${token}\\n`)\n\t\t}\n\n\t\tsaveSpinner.stop('Token saved successfully!')\n\n\t\t// Display user info\n\t\tclack.log.message('')\n\t\tclack.log.success('Authenticated as:')\n\t\tclack.log.message(\n\t\t\t` ${pc.cyan('User:')} ${verifyData.user.name || verifyData.user.email}`,\n\t\t)\n\t\tclack.log.message(` ${pc.cyan('Email:')} ${verifyData.user.email}`)\n\n\t\tif (verifyData.organizations && verifyData.organizations.length > 0) {\n\t\t\tclack.log.message('')\n\t\t\tclack.log.message(`${pc.cyan('Organizations:')}`)\n\t\t\tfor (const org of verifyData.organizations) {\n\t\t\t\tclack.log.message(` • ${org.name} ${pc.dim(`(${org.role})`)}`)\n\t\t\t}\n\t\t}\n\n\t\toutro(`\\nYou can now upload tasks with ${pc.cyan('thyme upload')}`)\n\t} catch (err) {\n\t\tspinner.stop('Failed to verify token')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n}\n","import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { config } from 'dotenv'\n\n/**\n * Load environment variables from .env file\n */\nexport function loadEnv(projectRoot: string): void {\n\tconst envPath = join(projectRoot, '.env')\n\tif (existsSync(envPath)) {\n\t\tconfig({ path: envPath })\n\t}\n}\n\n/**\n * Get environment variable with fallback\n */\nexport function getEnv(key: string, fallback?: string): string | undefined {\n\treturn process.env[key] ?? fallback\n}\n\n/**\n * Get required environment variable\n */\nexport function getRequiredEnv(key: string): string {\n\tconst value = process.env[key]\n\tif (!value) {\n\t\tthrow new Error(`Missing required environment variable: ${key}`)\n\t}\n\treturn value\n}\n","import { existsSync } from 'node:fs'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { isThymeProject } from '../utils/tasks'\nimport { clack, error, intro, outro, pc } from '../utils/ui'\n\nexport async function newCommand(taskName?: string) {\n\tintro('Thyme CLI - Create New Task')\n\n\tconst projectRoot = process.cwd()\n\n\t// Check if we're in a Thyme project\n\tif (!isThymeProject(projectRoot)) {\n\t\terror('Not in a Thyme project. Run `thyme init` first.')\n\t\tprocess.exit(1)\n\t}\n\n\t// Prompt for task name if not provided\n\tlet finalTaskName = taskName\n\tif (!finalTaskName) {\n\t\tconst name = await clack.text({\n\t\t\tmessage: 'What is your task name?',\n\t\t\tplaceholder: 'my-task',\n\t\t\tvalidate: (value) => {\n\t\t\t\tif (!value) return 'Task name is required'\n\t\t\t\tif (!/^[a-z0-9-]+$/.test(value))\n\t\t\t\t\treturn 'Task name must be lowercase alphanumeric with hyphens'\n\t\t\t},\n\t\t})\n\n\t\tif (clack.isCancel(name)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tfinalTaskName = name as string\n\t}\n\n\tconst taskPath = join(projectRoot, 'functions', finalTaskName)\n\n\t// Check if task already exists\n\tif (existsSync(taskPath)) {\n\t\terror(`Task \"${finalTaskName}\" already exists`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Creating task...')\n\n\ttry {\n\t\t// Create task directory\n\t\tawait mkdir(taskPath, { recursive: true })\n\n\t\t// Create index.ts\n\t\tconst indexTs = `import { defineTask, z } from '@thyme-sh/sdk'\nimport { encodeFunctionData } from 'viem'\n\nexport default defineTask({\n\tschema: z.object({\n\t\ttargetAddress: z.address(),\n\t}),\n\n\tasync run(ctx) {\n\t\tconst { targetAddress } = ctx.args\n\n\t\t// Your task logic here\n\t\tconsole.log('Running task with address:', targetAddress)\n\n\t\t// Example: Read from blockchain using the public client\n\t\t// const balance = await ctx.client.getBalance({ address: targetAddress })\n\t\t// const blockNumber = await ctx.client.getBlockNumber()\n\t\t// const value = await ctx.client.readContract({\n\t\t// address: targetAddress,\n\t\t// abi: [...],\n\t\t// functionName: 'balanceOf',\n\t\t// args: [someAddress],\n\t\t// })\n\n\t\t// Example: Return calls to execute\n\t\treturn {\n\t\t\tcanExec: true,\n\t\t\tcalls: [\n\t\t\t\t{\n\t\t\t\t\tto: targetAddress,\n\t\t\t\t\tdata: '0x' as const,\n\t\t\t\t},\n\t\t\t],\n\t\t}\n\n\t\t// Example with encodeFunctionData:\n\t\t// const abi = [...] as const\n\t\t// return {\n\t\t// canExec: true,\n\t\t// calls: [\n\t\t// {\n\t\t// to: targetAddress,\n\t\t// data: encodeFunctionData({\n\t\t// abi,\n\t\t// functionName: 'transfer',\n\t\t// args: [recipientAddress, 1000n],\n\t\t// }),\n\t\t// },\n\t\t// ],\n\t\t// }\n\n\t\t// Or return false if conditions not met\n\t\t// return {\n\t\t// canExec: false,\n\t\t// message: 'Conditions not met'\n\t\t// }\n\t},\n})\n`\n\n\t\tawait writeFile(join(taskPath, 'index.ts'), indexTs)\n\n\t\t// Create args.json\n\t\tconst args = {\n\t\t\ttargetAddress: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',\n\t\t}\n\n\t\tawait writeFile(join(taskPath, 'args.json'), JSON.stringify(args, null, 2))\n\n\t\tspinner.stop('Task created successfully!')\n\n\t\toutro(\n\t\t\t`${pc.green('✓')} Task \"${finalTaskName}\" created!\\n\\nNext steps:\\n ${pc.cyan('Edit')} functions/${finalTaskName}/index.ts\\n ${pc.cyan('Update')} functions/${finalTaskName}/args.json\\n ${pc.cyan('thyme run')} ${finalTaskName}`,\n\t\t)\n\t} catch (err) {\n\t\tspinner.stop('Failed to create task')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n}\n","import { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport type { Address } from 'viem'\nimport { http, createPublicClient, formatEther } from 'viem'\nimport { type TaskConfig, checkDeno, runInDeno } from '../deno/runner'\nimport { getEnv, loadEnv } from '../utils/env'\nimport {\n\tdiscoverTasks,\n\tgetTaskArgsPath,\n\tgetTaskPath,\n\tisThymeProject,\n} from '../utils/tasks'\nimport {\n\tclack,\n\terror,\n\tinfo,\n\tintro,\n\tlog,\n\toutro,\n\tpc,\n\tstep,\n\twarn,\n} from '../utils/ui'\n\ninterface RunOptions {\n\tsimulate?: boolean\n}\n\nexport async function runCommand(taskName?: string, options: RunOptions = {}) {\n\tintro('Thyme CLI - Run Task')\n\n\tconst projectRoot = process.cwd()\n\n\t// Load environment variables\n\tloadEnv(projectRoot)\n\n\t// Check if we're in a Thyme project\n\tif (!isThymeProject(projectRoot)) {\n\t\terror('Not in a Thyme project')\n\t\tprocess.exit(1)\n\t}\n\n\t// Check if Deno is installed\n\tconst hasDeno = await checkDeno()\n\tif (!hasDeno) {\n\t\terror('Deno is not installed. Please install Deno: https://deno.land/')\n\t\tprocess.exit(1)\n\t}\n\n\t// Discover tasks if no task name provided\n\tlet finalTaskName = taskName\n\tif (!finalTaskName) {\n\t\tconst tasks = await discoverTasks(projectRoot)\n\n\t\tif (tasks.length === 0) {\n\t\t\terror('No tasks found. Create one with `thyme new`')\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst selected = await clack.select({\n\t\t\tmessage: 'Select a task to run:',\n\t\t\toptions: tasks.map((task) => ({ value: task, label: task })),\n\t\t})\n\n\t\tif (clack.isCancel(selected)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tfinalTaskName = selected as string\n\t}\n\n\tconst taskPath = getTaskPath(projectRoot, finalTaskName)\n\tconst argsPath = getTaskArgsPath(projectRoot, finalTaskName)\n\n\t// Check if task exists\n\tif (!existsSync(taskPath)) {\n\t\terror(`Task \"${finalTaskName}\" not found`)\n\t\tprocess.exit(1)\n\t}\n\n\t// Use default config\n\tconst config: TaskConfig = {\n\t\tmemory: 128,\n\t\ttimeout: 30,\n\t\tnetwork: true,\n\t\trpcUrl: getEnv('RPC_URL'),\n\t}\n\n\t// Load args\n\tlet args: unknown = {}\n\tif (existsSync(argsPath)) {\n\t\ttry {\n\t\t\tconst argsData = await readFile(argsPath, 'utf-8')\n\t\t\targs = JSON.parse(argsData)\n\t\t} catch (err) {\n\t\t\twarn(\n\t\t\t\t`Failed to load args.json: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Executing task in Deno sandbox...')\n\n\t// Run task\n\tconst result = await runInDeno(taskPath, args, config)\n\n\tif (!result.success) {\n\t\tspinner.stop('Task execution failed')\n\t\terror(result.error ?? 'Unknown error')\n\t\tif (result.logs.length > 0) {\n\t\t\tstep('Task output:')\n\t\t\tfor (const taskLog of result.logs) {\n\t\t\t\tlog(` ${taskLog}`)\n\t\t\t}\n\t\t}\n\t\tprocess.exit(1)\n\t}\n\n\tspinner.stop('Task executed successfully')\n\n\t// Show logs\n\tif (result.logs.length > 0) {\n\t\tlog('')\n\t\tstep('Task output:')\n\t\tfor (const taskLog of result.logs) {\n\t\t\tlog(` ${taskLog}`)\n\t\t}\n\t}\n\n\t// Show result\n\tif (!result.result) {\n\t\terror('No result returned from task')\n\t\tprocess.exit(1)\n\t}\n\n\tlog('')\n\tif (result.result.canExec) {\n\t\tinfo(\n\t\t\t`${pc.green('✓')} Result: canExec = true (${result.result.calls.length} call(s))`,\n\t\t)\n\n\t\t// Show calls\n\t\tlog('')\n\t\tstep('Calls to execute:')\n\t\tfor (const call of result.result.calls) {\n\t\t\tlog(` ${pc.cyan('→')} to: ${call.to}`)\n\t\t\tlog(` data: ${call.data.slice(0, 20)}...`)\n\t\t}\n\n\t\t// Simulate if requested\n\t\tif (options.simulate) {\n\t\t\tlog('')\n\t\t\tawait simulateCalls(result.result.calls)\n\t\t}\n\t} else {\n\t\twarn('Result: canExec = false')\n\t\tinfo(`Message: ${result.result.message}`)\n\t}\n\n\t// Show execution stats\n\tlog('')\n\tif (\n\t\tresult.executionTime !== undefined ||\n\t\tresult.memoryUsed !== undefined ||\n\t\tresult.rpcRequestCount !== undefined\n\t) {\n\t\tstep('Execution stats:')\n\t\tif (result.executionTime !== undefined) {\n\t\t\tlog(` Duration: ${result.executionTime.toFixed(2)}ms`)\n\t\t}\n\t\tif (result.memoryUsed !== undefined) {\n\t\t\tconst memoryMB = (result.memoryUsed / 1024 / 1024).toFixed(2)\n\t\t\tlog(` Memory: ${memoryMB}MB`)\n\t\t}\n\t\tif (result.rpcRequestCount !== undefined) {\n\t\t\tlog(` RPC Requests: ${result.rpcRequestCount}`)\n\t\t}\n\t}\n\n\t// Show simulation tip if task can execute and simulation wasn't run\n\tif (result.result?.canExec && !options.simulate) {\n\t\tlog('')\n\t\tinfo(\n\t\t\t`${pc.dim('💡 Tip: Test calls on-chain with:')} ${pc.cyan(`thyme run ${finalTaskName} --simulate`)}`,\n\t\t)\n\t\toutro('')\n\t} else {\n\t\toutro('')\n\t}\n}\n\nasync function simulateCalls(\n\tcalls: Array<{ to: Address; data: `0x${string}` }>,\n) {\n\tconst rpcUrl = getEnv('RPC_URL')\n\tconst account = getEnv('SIMULATE_ACCOUNT')\n\n\tif (!rpcUrl || !account) {\n\t\twarn('Simulation requires RPC_URL and SIMULATE_ACCOUNT in .env file')\n\t\treturn\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Simulating on-chain...')\n\n\ttry {\n\t\tconst client = createPublicClient({\n\t\t\ttransport: http(rpcUrl),\n\t\t})\n\n\t\t// Get chain info\n\t\tconst chainId = await client.getChainId()\n\t\tconst blockNumber = await client.getBlockNumber()\n\n\t\tspinner.stop('Simulating on-chain...')\n\n\t\tlog('')\n\t\tinfo(`Chain ID: ${chainId}`)\n\t\tinfo(`Block: ${blockNumber}`)\n\t\tinfo(`Account: ${account}`)\n\n\t\t// Simulate each call\n\t\tconst simulationSpinner = clack.spinner()\n\t\tsimulationSpinner.start('Running simulation...')\n\n\t\tfor (const call of calls) {\n\t\t\ttry {\n\t\t\t\tawait client.call({\n\t\t\t\t\taccount: account as Address,\n\t\t\t\t\tto: call.to,\n\t\t\t\t\tdata: call.data,\n\t\t\t\t})\n\t\t\t} catch (err) {\n\t\t\t\tsimulationSpinner.stop('Simulation failed')\n\t\t\t\tlog('')\n\t\t\t\terror(\n\t\t\t\t\t`Call to ${call.to} would revert: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Get gas price\n\t\tconst gasPrice = await client.getGasPrice()\n\n\t\tsimulationSpinner.stop('Simulation successful!')\n\n\t\tlog('')\n\t\tstep('Simulation results:')\n\t\tlog(` ${pc.green('✓')} All calls would succeed`)\n\t\tlog(` Gas price: ${formatEther(gasPrice)} ETH`)\n\t} catch (err) {\n\t\tspinner.stop('Simulation failed')\n\t\tlog('')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t}\n}\n","import { spawn } from 'node:child_process'\nimport { dirname, resolve } from 'node:path'\nimport type { TaskResult } from '@thyme-sh/sdk'\n\nexport interface TaskConfig {\n\tmemory: number // MB\n\ttimeout: number // seconds\n\tnetwork: boolean\n\trpcUrl?: string // RPC URL for public client\n}\n\nexport interface RunResult {\n\tsuccess: boolean\n\tresult?: TaskResult\n\tlogs: string[]\n\terror?: string\n\texecutionTime?: number // milliseconds\n\tmemoryUsed?: number // bytes\n\trpcRequestCount?: number // number of RPC requests made\n}\n\n/**\n * Run a task in Deno sandbox - similar to Gelato's w3f test and @deno/sandbox\n * Creates an isolated Deno process with controlled permissions\n */\nexport async function runInDeno(\n\ttaskPath: string,\n\targs: unknown,\n\tconfig: TaskConfig,\n): Promise<RunResult> {\n\tconst taskDir = dirname(resolve(taskPath))\n\tconst absoluteTaskPath = resolve(taskPath)\n\n\tconst denoFlags = ['run', '--no-prompt']\n\n\t// Sandbox permissions - minimal by default, similar to @deno/sandbox\n\tdenoFlags.push(`--allow-read=${taskDir}`) // Only allow reading task directory\n\n\t// Add memory limit if specified\n\tif (config.memory) {\n\t\tdenoFlags.push(`--v8-flags=--max-old-space-size=${config.memory}`)\n\t}\n\n\t// Conditionally allow network (similar to allowNet in @deno/sandbox)\n\tif (config.network) {\n\t\tdenoFlags.push('--allow-net')\n\t}\n\n\t// Execute inline wrapper via stdin (similar to Gelato's approach)\n\tdenoFlags.push('-')\n\n\t// Execution wrapper that loads and runs the task\n\t// Similar to how Gelato's w3f test executes functions\n\tconst execScript = `\nimport task from '${absoluteTaskPath}';\nimport { createPublicClient, http } from 'npm:viem@2.21.54';\n\n// Create RPC request counter\nlet rpcRequestCount = 0;\n\n// Wrap the http transport to count requests\nconst countingHttp = (url) => {\n\tconst baseTransport = http(url);\n\treturn (config) => {\n\t\tconst transport = baseTransport(config);\n\t\treturn {\n\t\t\t...transport,\n\t\t\trequest: async (params) => {\n\t\t\t\trpcRequestCount++;\n\t\t\t\treturn transport.request(params);\n\t\t\t},\n\t\t};\n\t};\n};\n\n// Create public client for blockchain reads\nconst client = createPublicClient({\n\ttransport: countingHttp(${config.rpcUrl ? `'${config.rpcUrl}'` : 'undefined'}),\n});\n\nconst context = {\n\targs: ${JSON.stringify(args)},\n\tclient,\n};\n\ntry {\n\t// Track execution time and memory\n\tconst startTime = performance.now();\n\tconst startMemory = Deno.memoryUsage().heapUsed;\n\t\n\tconst result = await task.run(context);\n\t\n\tconst endTime = performance.now();\n\tconst endMemory = Deno.memoryUsage().heapUsed;\n\t\n\tconst executionTime = endTime - startTime;\n\tconst memoryUsed = endMemory - startMemory;\n\t\n\tconsole.log('__THYME_RESULT__' + JSON.stringify(result));\n\tconsole.log('__THYME_STATS__' + JSON.stringify({ executionTime, memoryUsed, rpcRequestCount }));\n} catch (error) {\n\tconsole.error('Task execution error:', error instanceof Error ? error.message : String(error));\n\tDeno.exit(1);\n}\n`\n\n\treturn new Promise((resolve) => {\n\t\tconst proc = spawn('deno', denoFlags, {\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\ttimeout: config.timeout * 1000,\n\t\t\tcwd: taskDir,\n\t\t})\n\n\t\tlet stdout = ''\n\t\tlet stderr = ''\n\t\tconst logs: string[] = []\n\n\t\t// Write the execution script to stdin\n\t\tproc.stdin?.write(execScript)\n\t\tproc.stdin?.end()\n\n\t\tproc.stdout?.on('data', (data) => {\n\t\t\tstdout += data.toString()\n\t\t})\n\n\t\tproc.stderr?.on('data', (data) => {\n\t\t\tstderr += data.toString()\n\t\t})\n\n\t\tproc.on('close', (code) => {\n\t\t\tif (code !== 0) {\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tlogs,\n\t\t\t\t\terror: stderr || `Process exited with code ${code}`,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Extract logs, result, and stats from stdout\n\t\t\t\tconst lines = stdout.trim().split('\\n')\n\t\t\t\tlet resultLine: string | undefined\n\t\t\t\tlet statsLine: string | undefined\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith('__THYME_RESULT__')) {\n\t\t\t\t\t\tresultLine = line.substring('__THYME_RESULT__'.length)\n\t\t\t\t\t} else if (line.startsWith('__THYME_STATS__')) {\n\t\t\t\t\t\tstatsLine = line.substring('__THYME_STATS__'.length)\n\t\t\t\t\t} else if (line.trim()) {\n\t\t\t\t\t\tlogs.push(line.trim())\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!resultLine) {\n\t\t\t\t\tthrow new Error('No result found in output')\n\t\t\t\t}\n\n\t\t\t\tconst result = JSON.parse(resultLine) as TaskResult\n\t\t\t\tconst stats = statsLine\n\t\t\t\t\t? JSON.parse(statsLine)\n\t\t\t\t\t: {\n\t\t\t\t\t\t\texecutionTime: undefined,\n\t\t\t\t\t\t\tmemoryUsed: undefined,\n\t\t\t\t\t\t\trpcRequestCount: undefined,\n\t\t\t\t\t\t}\n\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tresult,\n\t\t\t\t\tlogs,\n\t\t\t\t\texecutionTime: stats.executionTime,\n\t\t\t\t\tmemoryUsed: stats.memoryUsed,\n\t\t\t\t\trpcRequestCount: stats.rpcRequestCount,\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tlogs,\n\t\t\t\t\terror: `Failed to parse result: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tproc.on('error', (error) => {\n\t\t\tresolve({\n\t\t\t\tsuccess: false,\n\t\t\t\tlogs,\n\t\t\t\terror: `Failed to spawn Deno: ${error.message}`,\n\t\t\t})\n\t\t})\n\t})\n}\n\n/**\n * Check if Deno is installed\n */\nexport async function checkDeno(): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst proc = spawn('deno', ['--version'], { stdio: 'ignore' })\n\t\tproc.on('close', (code) => resolve(code === 0))\n\t\tproc.on('error', () => resolve(false))\n\t})\n}\n","import { existsSync } from 'node:fs'\nimport { bundleTask } from '../utils/bundler'\nimport { compressTask } from '../utils/compress'\nimport { getEnv, loadEnv } from '../utils/env'\nimport { extractSchemaFromTask } from '../utils/schema-extractor'\nimport { discoverTasks, getTaskPath, isThymeProject } from '../utils/tasks'\nimport { clack, error, info, intro, outro, pc, step, warn } from '../utils/ui'\n\nexport async function uploadCommand(taskName?: string, organizationId?: string) {\n\tintro('Thyme CLI - Upload Task')\n\n\tconst projectRoot = process.cwd()\n\n\t// Load environment variables\n\tloadEnv(projectRoot)\n\n\t// Check if we're in a Thyme project\n\tif (!isThymeProject(projectRoot)) {\n\t\terror('Not in a Thyme project')\n\t\tprocess.exit(1)\n\t}\n\n\t// Check for auth token\n\tconst authToken = getEnv('THYME_AUTH_TOKEN')\n\tif (!authToken) {\n\t\terror('Not authenticated. Run `thyme login` first.')\n\t\tprocess.exit(1)\n\t}\n\n\t// Get API URL (Convex deployment URL)\n\tconst apiUrl = getEnv('THYME_API_URL')\n\tif (!apiUrl) {\n\t\terror(\n\t\t\t'THYME_API_URL is not set. Please set it to your Convex deployment URL in .env',\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\t// Discover tasks if no task name provided\n\tlet finalTaskName = taskName\n\tif (!finalTaskName) {\n\t\tconst tasks = await discoverTasks(projectRoot)\n\n\t\tif (tasks.length === 0) {\n\t\t\terror('No tasks found. Create one with `thyme new`')\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst selected = await clack.select({\n\t\t\tmessage: 'Select a task to upload:',\n\t\t\toptions: tasks.map((task) => ({ value: task, label: task })),\n\t\t})\n\n\t\tif (clack.isCancel(selected)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tfinalTaskName = selected as string\n\t}\n\n\t// Fetch user's organizations\n\tconst orgSpinner = clack.spinner()\n\torgSpinner.start('Fetching organizations...')\n\n\tlet organizations: { id: string; name: string; role: string }[] = []\n\ttry {\n\t\tconst verifyResponse = await fetch(`${apiUrl}/api/auth/verify`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${authToken}`,\n\t\t\t},\n\t\t})\n\n\t\tif (!verifyResponse.ok) {\n\t\t\torgSpinner.stop('Failed to fetch organizations')\n\t\t\terror('Failed to authenticate. Please run `thyme login` again.')\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst verifyData = (await verifyResponse.json()) as {\n\t\t\tuser: {\n\t\t\t\tid: string\n\t\t\t\tname: string\n\t\t\t\temail: string\n\t\t\t}\n\t\t\torganizations: {\n\t\t\t\tid: string\n\t\t\t\tname: string\n\t\t\t\trole: string\n\t\t\t}[]\n\t\t}\n\n\t\torganizations = verifyData.organizations || []\n\t\torgSpinner.stop('Organizations loaded')\n\t} catch (err) {\n\t\torgSpinner.stop('Failed to fetch organizations')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n\n\t// Check if user has any organizations\n\tif (organizations.length === 0) {\n\t\terror('You are not a member of any organizations. Please create or join an organization first.')\n\t\tprocess.exit(1)\n\t}\n\n\t// Determine organization to upload to\n\tlet selectedOrgId = organizationId\n\n\t// If organization ID was provided, validate it\n\tif (selectedOrgId) {\n\t\tconst orgExists = organizations.find((org) => org.id === selectedOrgId)\n\t\tif (!orgExists) {\n\t\t\terror(`Organization with ID \"${selectedOrgId}\" not found or you don't have access to it.`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t} else {\n\t\t// Prompt user to select an organization\n\t\tconst selected = await clack.select({\n\t\t\tmessage: 'Select an organization to upload to:',\n\t\t\toptions: organizations.map((org) => ({\n\t\t\t\tvalue: org.id,\n\t\t\t\tlabel: `${org.name} ${pc.dim(`(${org.role})`)}`,\n\t\t\t})),\n\t\t})\n\n\t\tif (clack.isCancel(selected)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tselectedOrgId = selected as string\n\t}\n\n\tconst taskPath = getTaskPath(projectRoot, finalTaskName)\n\n\t// Check if task exists\n\tif (!existsSync(taskPath)) {\n\t\terror(`Task \"${finalTaskName}\" not found`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Bundling task...')\n\n\ttry {\n\t\t// Bundle task code with all dependencies\n\t\tconst { source, bundle } = await bundleTask(taskPath)\n\n\t\tspinner.message('Extracting schema...')\n\n\t\t// Extract schema from source code\n\t\tconst schema = extractSchemaFromTask(source)\n\n\t\tspinner.message('Compressing files...')\n\n\t\t// Compress source and bundle into ZIP\n\t\tconst { zipBuffer, checksum } = compressTask(source, bundle)\n\n\t\tspinner.message('Uploading to cloud...')\n\n\t\t// Create form data\n\t\tconst formData = new FormData()\n\n\t\t// Add metadata\n\t\tformData.append(\n\t\t\t'data',\n\t\t\tJSON.stringify({\n\t\t\t\torganizationId: selectedOrgId as string,\n\t\t\t\tcheckSum: checksum,\n\t\t\t\tschema: schema || undefined,\n\t\t\t}),\n\t\t)\n\n\t\t// Add ZIP blob\n\t\tformData.append('blob', new Blob([zipBuffer]), 'task.zip')\n\n\t\t// Upload to API\n\t\tconst response = await fetch(`${apiUrl}/api/task/upload`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${authToken}`,\n\t\t\t},\n\t\t\tbody: formData,\n\t\t})\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text()\n\t\t\tthrow new Error(`Upload failed: ${errorText}`)\n\t\t}\n\n\t\tconst result = await response.json()\n\n\t\tspinner.stop('Task uploaded successfully!')\n\n\t\tconst selectedOrg = organizations.find((org) => org.id === selectedOrgId)\n\n\t\tclack.log.message('')\n\t\tclack.log.success('Upload details:')\n\t\tclack.log.message(` ${pc.dim('Task:')} ${pc.cyan(finalTaskName)}`)\n\t\tclack.log.message(\n\t\t\t` ${pc.dim('Organization:')} ${pc.cyan(selectedOrg?.name || 'Unknown')}`,\n\t\t)\n\t\tclack.log.message(\n\t\t\t` ${pc.dim('Size:')} ${(zipBuffer.length / 1024).toFixed(2)} KB`,\n\t\t)\n\t\tclack.log.message(` ${pc.dim('Checksum:')} ${checksum.slice(0, 16)}...`)\n\t\tif (result.taskId) {\n\t\t\tclack.log.message(` ${pc.dim('Task ID:')} ${pc.green(result.taskId)}`)\n\t\t}\n\n\t\toutro(\n\t\t\t`${pc.green('✓')} Task uploaded!\\n\\n` +\n\t\t\t\t`Configure triggers in the dashboard: ${pc.cyan('https://thyme.sh/dashboard')}`,\n\t\t)\n\t} catch (err) {\n\t\tspinner.stop('Upload failed')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n}\n","import { readFile } from 'node:fs/promises'\nimport { build } from 'esbuild'\n\nexport interface BundleResult {\n\tsource: string\n\tbundle: string\n}\n\n/**\n * Bundle task code with all dependencies using esbuild\n * Target: ESM format for Deno compatibility\n */\nexport async function bundleTask(taskPath: string): Promise<BundleResult> {\n\t// Read original source\n\tconst source = await readFile(taskPath, 'utf-8')\n\n\t// Node.js built-in modules that should not be bundled\n\tconst nodeBuiltins = [\n\t\t'assert',\n\t\t'buffer',\n\t\t'child_process',\n\t\t'cluster',\n\t\t'crypto',\n\t\t'dgram',\n\t\t'dns',\n\t\t'events',\n\t\t'fs',\n\t\t'http',\n\t\t'http2',\n\t\t'https',\n\t\t'net',\n\t\t'os',\n\t\t'path',\n\t\t'perf_hooks',\n\t\t'process',\n\t\t'querystring',\n\t\t'readline',\n\t\t'stream',\n\t\t'string_decoder',\n\t\t'timers',\n\t\t'tls',\n\t\t'tty',\n\t\t'url',\n\t\t'util',\n\t\t'v8',\n\t\t'vm',\n\t\t'zlib',\n\t\t// Node: prefix versions\n\t\t'node:assert',\n\t\t'node:buffer',\n\t\t'node:child_process',\n\t\t'node:cluster',\n\t\t'node:crypto',\n\t\t'node:dgram',\n\t\t'node:dns',\n\t\t'node:events',\n\t\t'node:fs',\n\t\t'node:http',\n\t\t'node:http2',\n\t\t'node:https',\n\t\t'node:net',\n\t\t'node:os',\n\t\t'node:path',\n\t\t'node:perf_hooks',\n\t\t'node:process',\n\t\t'node:querystring',\n\t\t'node:readline',\n\t\t'node:stream',\n\t\t'node:string_decoder',\n\t\t'node:timers',\n\t\t'node:tls',\n\t\t'node:tty',\n\t\t'node:url',\n\t\t'node:util',\n\t\t'node:v8',\n\t\t'node:vm',\n\t\t'node:zlib',\n\t]\n\n\t// Bundle with esbuild\n\tconst result = await build({\n\t\tentryPoints: [taskPath],\n\t\tbundle: true,\n\t\tformat: 'esm',\n\t\tplatform: 'neutral',\n\t\ttarget: 'esnext',\n\t\twrite: false,\n\t\ttreeShaking: true,\n\t\tminify: false, // Keep readable for debugging\n\t\tsourcemap: false,\n\t\texternal: nodeBuiltins, // Don't bundle Node.js built-ins\n\t\tlogLevel: 'silent',\n\t})\n\n\tif (result.outputFiles.length === 0) {\n\t\tthrow new Error('No output from bundler')\n\t}\n\n\tconst bundle = result.outputFiles[0].text\n\n\treturn {\n\t\tsource,\n\t\tbundle,\n\t}\n}\n","import { compressTask as sdkCompressTask } from '@thyme-sh/sdk'\n\nexport interface CompressResult {\n\tzipBuffer: Buffer\n\tchecksum: string\n}\n\n/**\n * Compress source and bundle into a ZIP archive\n * Uses SDK's compression function with fflate\n */\nexport function compressTask(source: string, bundle: string): CompressResult {\n\tconst { zipBuffer, checksum } = sdkCompressTask(source, bundle)\n\n\t// Convert Uint8Array to Buffer for Node.js\n\treturn {\n\t\tzipBuffer: Buffer.from(zipBuffer),\n\t\tchecksum,\n\t}\n}\n","import { z } from 'zod'\n\n/**\n * Extract Zod schema from task code and convert to JSON Schema\n * This allows the frontend to generate forms for task arguments\n */\nexport function extractSchemaFromTask(taskCode: string): string | null {\n\ttry {\n\t\t// Create a sandbox to safely evaluate the task code\n\t\t// We'll extract the schema by running the code and capturing the schema definition\n\t\tconst schemaExtractor = `\n const { z } = require('zod');\n const { zodToJsonSchema } = require('zod-to-json-schema');\n \n // Extended z with address validator\n const zodExtended = {\n ...z,\n address: () => z.string().refine((val) => /^0x[a-fA-F0-9]{40}$/.test(val), {\n message: 'Invalid Ethereum address',\n }),\n };\n \n // Mock defineTask to capture schema\n let capturedSchema = null;\n const defineTask = (definition) => {\n if (definition.schema) {\n capturedSchema = definition.schema;\n }\n return definition;\n };\n \n // Mock viem exports\n const encodeFunctionData = () => '0x';\n \n // Evaluate task code\n ${taskCode}\n \n // Convert schema to JSON Schema\n if (capturedSchema) {\n const jsonSchema = zodToJsonSchema(capturedSchema, { target: 'openApi3' });\n console.log(JSON.stringify(jsonSchema));\n } else {\n console.log('null');\n }\n `\n\n\t\t// For now, return a simple extraction by parsing the code\n\t\t// This is a simplified version - in production you might want to use a proper parser\n\t\tconst schemaMatch = taskCode.match(/schema:\\s*z\\.object\\(\\{([^}]+)\\}\\)/)\n\n\t\tif (!schemaMatch) {\n\t\t\treturn null\n\t\t}\n\n\t\t// Parse the schema fields\n\t\tconst schemaContent = schemaMatch[1]\n\t\tconst fields: Record<string, unknown> = {}\n\n\t\t// Simple regex to extract field definitions\n\t\tconst fieldMatches = schemaContent.matchAll(\n\t\t\t/(\\w+):\\s*z\\.(\\w+)\\(\\)/g,\n\t\t)\n\n\t\tfor (const match of fieldMatches) {\n\t\t\tconst [, fieldName, fieldType] = match\n\t\t\tif (fieldName && fieldType) {\n\t\t\t\t// Convert Zod types to JSON Schema types\n\t\t\t\tlet jsonType = 'string'\n\t\t\t\tswitch (fieldType) {\n\t\t\t\t\tcase 'string':\n\t\t\t\t\t\tjsonType = 'string'\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'number':\n\t\t\t\t\t\tjsonType = 'number'\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'boolean':\n\t\t\t\t\t\tjsonType = 'boolean'\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'address':\n\t\t\t\t\t\tjsonType = 'string'\n\t\t\t\t\t\tbreak\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tjsonType = 'string'\n\t\t\t\t}\n\n\t\t\t\tfields[fieldName] = {\n\t\t\t\t\ttype: jsonType,\n\t\t\t\t\t...(fieldType === 'address' && {\n\t\t\t\t\t\tpattern: '^0x[a-fA-F0-9]{40}$',\n\t\t\t\t\t\tdescription: 'Ethereum address',\n\t\t\t\t\t}),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Object.keys(fields).length === 0) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst jsonSchema = {\n\t\t\ttype: 'object',\n\t\t\tproperties: fields,\n\t\t\trequired: Object.keys(fields),\n\t\t}\n\n\t\treturn JSON.stringify(jsonSchema)\n\t} catch (err) {\n\t\tconsole.error('Error extracting schema:', err)\n\t\treturn null\n\t}\n}\n"],"mappings":"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","names":["intro","outro","log","intro","outro","existsSync","join","intro","outro","existsSync","writeFile","join","existsSync","join","intro","join","existsSync","writeFile","outro","existsSync","mkdir","writeFile","join","intro","join","existsSync","mkdir","writeFile","outro","existsSync","readFile","config","resolve","error","intro","existsSync","config","readFile","log","outro","existsSync","readFile","intro","existsSync","outro"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/ui.ts","../src/utils/tasks.ts","../src/commands/list.ts","../src/commands/login.ts","../src/utils/env.ts","../src/commands/new.ts","../src/commands/run.ts","../src/deno/runner.ts","../src/commands/upload.ts","../src/utils/bundler.ts","../src/utils/compress.ts","../src/utils/schema-extractor.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { Command } from 'commander'\nimport { initCommand } from './commands/init'\nimport { listCommand } from './commands/list'\nimport { loginCommand } from './commands/login'\nimport { newCommand } from './commands/new'\nimport { runCommand } from './commands/run'\nimport { uploadCommand } from './commands/upload'\n\n// Read version from package.json dynamically\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nlet version = '0.0.0'\ntry {\n\tconst packageJson = JSON.parse(\n\t\treadFileSync(join(__dirname, '../package.json'), 'utf-8'),\n\t)\n\tversion = packageJson.version || version\n} catch {\n\t// Fallback if package.json can't be read (e.g., in bundled builds)\n\t// Try one more level up for bundled scenarios\n\ttry {\n\t\tconst packageJson = JSON.parse(\n\t\t\treadFileSync(join(__dirname, '../../package.json'), 'utf-8'),\n\t\t)\n\t\tversion = packageJson.version || version\n\t} catch {\n\t\t// Use default version\n\t}\n}\n\nconst program = new Command()\n\nprogram\n\t.name('thyme')\n\t.description('CLI for developing and deploying Thyme tasks')\n\t.version(version)\n\nprogram\n\t.command('init')\n\t.description('Initialize a new Thyme project')\n\t.argument('[name]', 'Project name')\n\t.action(initCommand)\n\nprogram\n\t.command('new')\n\t.description('Create a new task')\n\t.argument('[name]', 'Task name')\n\t.action(newCommand)\n\nprogram\n\t.command('run')\n\t.description('Run a task locally')\n\t.argument('[task]', 'Task name')\n\t.option('--simulate', 'Simulate on-chain execution')\n\t.action((task, options) => runCommand(task, options))\n\nprogram.command('list').description('List all tasks').action(listCommand)\n\nprogram\n\t.command('login')\n\t.description('Authenticate with Thyme Cloud')\n\t.action(loginCommand)\n\nprogram\n\t.command('upload')\n\t.description('Upload a task to Thyme Cloud')\n\t.argument('[task]', 'Task name')\n\t.option('-o, --organization <id>', 'Organization ID to upload to')\n\t.action((task, options) => uploadCommand(task, options.organization))\n\nprogram.parse()\n","import { existsSync } from 'node:fs'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { clack, error, intro, outro, pc } from '../utils/ui'\n\nexport async function initCommand(projectName?: string) {\n\tintro('Thyme CLI - Initialize Project')\n\n\t// Prompt for project name if not provided\n\tlet finalProjectName = projectName\n\tif (!finalProjectName) {\n\t\tconst name = await clack.text({\n\t\t\tmessage: 'What is your project name?',\n\t\t\tplaceholder: 'my-thyme-project',\n\t\t\tvalidate: (value) => {\n\t\t\t\tif (!value) return 'Project name is required'\n\t\t\t\tif (!/^[a-z0-9-]+$/.test(value))\n\t\t\t\t\treturn 'Project name must be lowercase alphanumeric with hyphens'\n\t\t\t},\n\t\t})\n\n\t\tif (clack.isCancel(name)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tfinalProjectName = name as string\n\t}\n\n\tconst projectPath = join(process.cwd(), finalProjectName)\n\n\t// Check if directory exists\n\tif (existsSync(projectPath)) {\n\t\terror(`Directory \"${finalProjectName}\" already exists`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Creating project structure...')\n\n\ttry {\n\t\t// Create directories\n\t\tawait mkdir(projectPath, { recursive: true })\n\t\tawait mkdir(join(projectPath, 'functions'), { recursive: true })\n\n\t\t// Create package.json\n\t\tconst packageJson = {\n\t\t\tname: finalProjectName,\n\t\t\tversion: '0.1.0',\n\t\t\ttype: 'module',\n\t\t\tprivate: true,\n\t\t\tscripts: {\n\t\t\t\tdev: 'thyme run',\n\t\t\t},\n\t\t\tdependencies: {\n\t\t\t\t'@thyme-sh/sdk': '^0.1.0',\n\t\t\t\tviem: '^2.21.54',\n\t\t\t\tzod: '^3.24.1',\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t'@thyme-sh/cli': '^0.1.0',\n\t\t\t\ttypescript: '^5.7.2',\n\t\t\t},\n\t\t}\n\n\t\tawait writeFile(\n\t\t\tjoin(projectPath, 'package.json'),\n\t\t\tJSON.stringify(packageJson, null, 2),\n\t\t)\n\n\t\t// Create tsconfig.json\n\t\tconst tsconfig = {\n\t\t\tcompilerOptions: {\n\t\t\t\ttarget: 'ES2022',\n\t\t\t\tmodule: 'ESNext',\n\t\t\t\tmoduleResolution: 'bundler',\n\t\t\t\tlib: ['ES2022', 'DOM'],\n\t\t\t\tstrict: true,\n\t\t\t\tesModuleInterop: true,\n\t\t\t\tskipLibCheck: true,\n\t\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\t\tresolveJsonModule: true,\n\t\t\t},\n\t\t\tinclude: ['functions/**/*'],\n\t\t}\n\n\t\tawait writeFile(\n\t\t\tjoin(projectPath, 'tsconfig.json'),\n\t\t\tJSON.stringify(tsconfig, null, 2),\n\t\t)\n\n\t\t// Create .env.example\n\t\tconst envExample = `# Simulation settings (for --simulate flag)\nRPC_URL=https://eth-sepolia.g.alchemy.com/v2/your-key\nSIMULATE_ACCOUNT=0x742d35Cc6634C0532925a3b844Bc454e4438f44e\n\n# Cloud authentication (set by \\`thyme login\\`)\nTHYME_AUTH_TOKEN=\n\n# Cloud API URL (required - your Convex deployment URL)\n# Example: https://your-deployment.convex.cloud\nTHYME_API_URL=\n`\n\n\t\tawait writeFile(join(projectPath, '.env.example'), envExample)\n\n\t\t// Create .gitignore\n\t\tconst gitignore = `node_modules/\ndist/\n.env\n.env.local\n*.log\n`\n\n\t\tawait writeFile(join(projectPath, '.gitignore'), gitignore)\n\n\t\t// Create README\n\t\tconst readme = `# ${finalProjectName}\n\nA Thyme project for Web3 automation tasks.\n\n## Getting Started\n\n\\`\\`\\`bash\n# Install dependencies\nnpm install\n\n# Create a new task\nthyme new my-task\n\n# Run a task locally\nthyme run my-task\n\n# Simulate on-chain\nthyme run my-task --simulate\n\n# Deploy to cloud\nthyme login\nthyme upload my-task\n\\`\\`\\`\n\n## Project Structure\n\n\\`\\`\\`\nfunctions/\n my-task/\n index.ts # Task definition\n args.json # Test arguments\n\\`\\`\\`\n`\n\n\t\tawait writeFile(join(projectPath, 'README.md'), readme)\n\n\t\tspinner.stop('Project created successfully!')\n\n\t\toutro(\n\t\t\t`${pc.green('✓')} Project initialized!\\n\\nNext steps:\\n ${pc.cyan('cd')} ${finalProjectName}\\n ${pc.cyan('npm install')}\\n ${pc.cyan('thyme new')} my-task\\n ${pc.cyan('thyme run')} my-task`,\n\t\t)\n\t} catch (err) {\n\t\tspinner.stop('Failed to create project')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n}\n","import * as clack from '@clack/prompts'\nimport pc from 'picocolors'\n\nexport { clack, pc }\n\nexport function intro(title: string) {\n\tclack.intro(pc.bgCyan(pc.black(` ${title} `)))\n}\n\nexport function outro(message: string) {\n\tclack.outro(message)\n}\n\nexport function error(message: string) {\n\tclack.log.error(pc.red(message))\n}\n\nexport function info(message: string) {\n\tclack.log.info(pc.cyan(message))\n}\n\nexport function warn(message: string) {\n\tclack.log.warn(pc.yellow(message))\n}\n\nexport function step(message: string) {\n\tclack.log.step(message)\n}\n\nexport function log(message: string) {\n\tclack.log.message(message)\n}\n","import { existsSync } from 'node:fs'\nimport { readdir } from 'node:fs/promises'\nimport { join, resolve } from 'node:path'\n\n/**\n * Regex pattern for valid task names\n * Only lowercase alphanumeric characters and hyphens allowed\n */\nconst VALID_TASK_NAME_PATTERN = /^[a-z0-9-]+$/\n\n/**\n * Maximum length for task names\n */\nconst MAX_TASK_NAME_LENGTH = 64\n\n/**\n * Validate a task name for security and consistency\n * Prevents path traversal and ensures valid naming conventions\n *\n * @throws Error if task name is invalid\n */\nexport function validateTaskName(taskName: string): void {\n\tif (!taskName) {\n\t\tthrow new Error('Task name is required')\n\t}\n\n\tif (taskName.length > MAX_TASK_NAME_LENGTH) {\n\t\tthrow new Error(\n\t\t\t`Task name too long: ${taskName.length} characters (max: ${MAX_TASK_NAME_LENGTH})`,\n\t\t)\n\t}\n\n\t// Check for path traversal attempts\n\tif (\n\t\ttaskName.includes('..') ||\n\t\ttaskName.includes('/') ||\n\t\ttaskName.includes('\\\\')\n\t) {\n\t\tthrow new Error('Invalid task name: path traversal characters not allowed')\n\t}\n\n\t// Check for valid characters\n\tif (!VALID_TASK_NAME_PATTERN.test(taskName)) {\n\t\tthrow new Error(\n\t\t\t'Task name must be lowercase alphanumeric with hyphens only',\n\t\t)\n\t}\n\n\t// Check for reserved names\n\tconst reservedNames = ['node_modules', 'dist', 'build', 'src', 'lib']\n\tif (reservedNames.includes(taskName)) {\n\t\tthrow new Error(`Task name \"${taskName}\" is reserved`)\n\t}\n}\n\n/**\n * Discover all tasks in the functions directory\n */\nexport async function discoverTasks(projectRoot: string): Promise<string[]> {\n\tconst functionsDir = join(projectRoot, 'functions')\n\n\tif (!existsSync(functionsDir)) {\n\t\treturn []\n\t}\n\n\ttry {\n\t\tconst entries = await readdir(functionsDir, { withFileTypes: true })\n\n\t\treturn entries\n\t\t\t.filter((e) => e.isDirectory())\n\t\t\t.filter((e) => {\n\t\t\t\t// Validate task name format\n\t\t\t\ttry {\n\t\t\t\t\tvalidateTaskName(e.name)\n\t\t\t\t\treturn existsSync(join(functionsDir, e.name, 'index.ts'))\n\t\t\t\t} catch {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t})\n\t\t\t.map((e) => e.name)\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Get the path to a task's index file\n * Includes path traversal protection\n *\n * @throws Error if task name is invalid or path escapes functions directory\n */\nexport function getTaskPath(projectRoot: string, taskName: string): string {\n\t// Validate task name first\n\tvalidateTaskName(taskName)\n\n\tconst functionsDir = resolve(projectRoot, 'functions')\n\tconst taskPath = resolve(functionsDir, taskName, 'index.ts')\n\n\t// Ensure the resolved path is within the functions directory\n\tif (!taskPath.startsWith(functionsDir)) {\n\t\tthrow new Error('Invalid task path: path traversal detected')\n\t}\n\n\treturn taskPath\n}\n\n/**\n * Get the path to a task's args file\n * Includes path traversal protection\n *\n * @throws Error if task name is invalid or path escapes functions directory\n */\nexport function getTaskArgsPath(projectRoot: string, taskName: string): string {\n\t// Validate task name first\n\tvalidateTaskName(taskName)\n\n\tconst functionsDir = resolve(projectRoot, 'functions')\n\tconst argsPath = resolve(functionsDir, taskName, 'args.json')\n\n\t// Ensure the resolved path is within the functions directory\n\tif (!argsPath.startsWith(functionsDir)) {\n\t\tthrow new Error('Invalid task path: path traversal detected')\n\t}\n\n\treturn argsPath\n}\n\n/**\n * Check if we're in a Thyme project\n * Validates by checking for functions directory AND package.json with @thyme-sh/sdk\n */\nexport function isThymeProject(projectRoot: string): boolean {\n\tconst functionsDir = join(projectRoot, 'functions')\n\tconst packageJsonPath = join(projectRoot, 'package.json')\n\n\t// Must have functions directory\n\tif (!existsSync(functionsDir)) {\n\t\treturn false\n\t}\n\n\t// Check for package.json with thyme SDK dependency\n\tif (existsSync(packageJsonPath)) {\n\t\ttry {\n\t\t\t// Synchronous check for simplicity in this validation function\n\t\t\tconst fs = require('node:fs')\n\t\t\tconst packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\t\t\tconst deps = {\n\t\t\t\t...packageJson.dependencies,\n\t\t\t\t...packageJson.devDependencies,\n\t\t\t}\n\t\t\treturn '@thyme-sh/sdk' in deps || '@thyme-sh/cli' in deps\n\t\t} catch {\n\t\t\t// If we can't read package.json, fall back to just checking functions dir\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn true\n}\n","import { discoverTasks, isThymeProject } from '../utils/tasks'\nimport { clack, intro, outro, pc, step } from '../utils/ui'\n\nexport async function listCommand() {\n\tintro('Thyme CLI - List Tasks')\n\n\tconst projectRoot = process.cwd()\n\n\tif (!isThymeProject(projectRoot)) {\n\t\toutro(pc.red('Not in a Thyme project'))\n\t\tprocess.exit(1)\n\t}\n\n\tconst tasks = await discoverTasks(projectRoot)\n\n\tif (tasks.length === 0) {\n\t\toutro(pc.yellow('No tasks found. Create one with `thyme new`'))\n\t\treturn\n\t}\n\n\tstep(`Found ${tasks.length} task(s):`)\n\tfor (const task of tasks) {\n\t\tclack.log.message(` ${pc.cyan('●')} ${task}`)\n\t}\n\n\toutro('')\n}\n","import { existsSync } from 'node:fs'\nimport { appendFile, readFile, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { z } from 'zod'\nimport { getEnv, loadEnv } from '../utils/env'\nimport { clack, error, info, intro, outro, pc } from '../utils/ui'\n\n// Zod schema for API response validation\nconst verifyResponseSchema = z.object({\n\tuser: z.object({\n\t\tid: z.string(),\n\t\tname: z.string().optional().default(''),\n\t\temail: z.string(),\n\t}),\n\torganizations: z\n\t\t.array(\n\t\t\tz.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tname: z.string(),\n\t\t\t\trole: z.string(),\n\t\t\t}),\n\t\t)\n\t\t.optional()\n\t\t.default([]),\n})\n\nexport async function loginCommand() {\n\tintro('Thyme CLI - Login')\n\n\tconst projectRoot = process.cwd()\n\tconst envPath = join(projectRoot, '.env')\n\n\t// Load environment variables\n\tloadEnv(projectRoot)\n\n\t// Show instructions\n\tinfo('To authenticate with Thyme Cloud:')\n\tclack.log.message(\n\t\t` 1. Visit ${pc.cyan('https://thyme.sh/settings/api-keys')}`,\n\t)\n\tclack.log.message(' 2. Generate a new API token')\n\tclack.log.message(' 3. Copy the token and paste it below')\n\tclack.log.message('')\n\n\t// Prompt for token\n\tconst token = await clack.password({\n\t\tmessage: 'Paste your API token:',\n\t\tvalidate: (value) => {\n\t\t\tif (!value) return 'Token is required'\n\t\t\tif (value.length < 10) return 'Token seems too short'\n\t\t},\n\t})\n\n\tif (clack.isCancel(token)) {\n\t\tclack.cancel('Operation cancelled')\n\t\tprocess.exit(0)\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Verifying token...')\n\n\ttry {\n\t\t// Get API URL (Convex deployment URL)\n\t\tconst apiUrl = getEnv('THYME_API_URL')\n\n\t\tif (!apiUrl) {\n\t\t\tspinner.stop('Configuration error')\n\t\t\terror(\n\t\t\t\t'THYME_API_URL is not set. Please set it to your Convex deployment URL (e.g., https://your-deployment.convex.cloud)',\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\t// Verify token with API\n\t\tconst verifyResponse = await fetch(`${apiUrl}/api/auth/verify`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t},\n\t\t})\n\n\t\tif (!verifyResponse.ok) {\n\t\t\tspinner.stop('Token verification failed')\n\t\t\tconst errorText = await verifyResponse.text()\n\t\t\terror(`Invalid token: ${errorText}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst rawData = await verifyResponse.json()\n\n\t\t// Validate response with Zod\n\t\tconst parseResult = verifyResponseSchema.safeParse(rawData)\n\t\tif (!parseResult.success) {\n\t\t\tspinner.stop('Invalid API response')\n\t\t\terror(`API returned unexpected data format: ${parseResult.error.message}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst verifyData = parseResult.data\n\n\t\tspinner.stop('Token verified!')\n\n\t\t// Save token\n\t\tconst saveSpinner = clack.spinner()\n\t\tsaveSpinner.start('Saving token...')\n\n\t\t// Read existing .env or create new\n\t\tlet envContent = ''\n\t\tif (existsSync(envPath)) {\n\t\t\tenvContent = await readFile(envPath, 'utf-8')\n\t\t}\n\n\t\t// Check if THYME_AUTH_TOKEN already exists\n\t\tconst tokenRegex = /^THYME_AUTH_TOKEN=.*$/m\n\t\tif (tokenRegex.test(envContent)) {\n\t\t\t// Replace existing token\n\t\t\tenvContent = envContent.replace(tokenRegex, `THYME_AUTH_TOKEN=${token}`)\n\t\t\tawait writeFile(envPath, envContent)\n\t\t} else {\n\t\t\t// Append new token\n\t\t\tconst newLine = envContent && !envContent.endsWith('\\n') ? '\\n' : ''\n\t\t\tawait appendFile(envPath, `${newLine}THYME_AUTH_TOKEN=${token}\\n`)\n\t\t}\n\n\t\tsaveSpinner.stop('Token saved successfully!')\n\n\t\t// Display user info\n\t\tclack.log.message('')\n\t\tclack.log.success('Authenticated as:')\n\t\tclack.log.message(\n\t\t\t` ${pc.cyan('User:')} ${verifyData.user.name || verifyData.user.email}`,\n\t\t)\n\t\tclack.log.message(` ${pc.cyan('Email:')} ${verifyData.user.email}`)\n\n\t\tif (verifyData.organizations && verifyData.organizations.length > 0) {\n\t\t\tclack.log.message('')\n\t\t\tclack.log.message(`${pc.cyan('Organizations:')}`)\n\t\t\tfor (const org of verifyData.organizations) {\n\t\t\t\tclack.log.message(` • ${org.name} ${pc.dim(`(${org.role})`)}`)\n\t\t\t}\n\t\t}\n\n\t\toutro(`\\nYou can now upload tasks with ${pc.cyan('thyme upload')}`)\n\t} catch (err) {\n\t\tspinner.stop('Failed to verify token')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n}\n","import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { config } from 'dotenv'\n\n/**\n * Load environment variables from .env file\n */\nexport function loadEnv(projectRoot: string): void {\n\tconst envPath = join(projectRoot, '.env')\n\tif (existsSync(envPath)) {\n\t\tconfig({ path: envPath })\n\t}\n}\n\n/**\n * Get environment variable with fallback\n */\nexport function getEnv(key: string, fallback?: string): string | undefined {\n\treturn process.env[key] ?? fallback\n}\n\n/**\n * Get required environment variable\n */\nexport function getRequiredEnv(key: string): string {\n\tconst value = process.env[key]\n\tif (!value) {\n\t\tthrow new Error(`Missing required environment variable: ${key}`)\n\t}\n\treturn value\n}\n","import { existsSync } from 'node:fs'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { isThymeProject, validateTaskName } from '../utils/tasks'\nimport { clack, error, intro, outro, pc } from '../utils/ui'\n\nexport async function newCommand(taskName?: string) {\n\tintro('Thyme CLI - Create New Task')\n\n\tconst projectRoot = process.cwd()\n\n\t// Check if we're in a Thyme project\n\tif (!isThymeProject(projectRoot)) {\n\t\terror('Not in a Thyme project. Run `thyme init` first.')\n\t\tprocess.exit(1)\n\t}\n\n\t// Prompt for task name if not provided\n\tlet finalTaskName = taskName\n\n\t// Validate task name if provided via CLI argument\n\tif (finalTaskName) {\n\t\ttry {\n\t\t\tvalidateTaskName(finalTaskName)\n\t\t} catch (err) {\n\t\t\terror(err instanceof Error ? err.message : String(err))\n\t\t\tprocess.exit(1)\n\t\t}\n\t} else {\n\t\tconst name = await clack.text({\n\t\t\tmessage: 'What is your task name?',\n\t\t\tplaceholder: 'my-task',\n\t\t\tvalidate: (value) => {\n\t\t\t\ttry {\n\t\t\t\t\tvalidateTaskName(value)\n\t\t\t\t} catch (err) {\n\t\t\t\t\treturn err instanceof Error ? err.message : String(err)\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\n\t\tif (clack.isCancel(name)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tfinalTaskName = name as string\n\t}\n\n\tconst taskPath = join(projectRoot, 'functions', finalTaskName)\n\n\t// Check if task already exists\n\tif (existsSync(taskPath)) {\n\t\terror(`Task \"${finalTaskName}\" already exists`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Creating task...')\n\n\ttry {\n\t\t// Create task directory\n\t\tawait mkdir(taskPath, { recursive: true })\n\n\t\t// Create index.ts\n\t\tconst indexTs = `import { defineTask, z } from '@thyme-sh/sdk'\nimport { encodeFunctionData } from 'viem'\n\nexport default defineTask({\n\tschema: z.object({\n\t\ttargetAddress: z.address(),\n\t}),\n\n\tasync run(ctx) {\n\t\tconst { targetAddress } = ctx.args\n\t\tconst { logger } = ctx\n\n\t\t// Use the logger to output messages to the Thyme dashboard\n\t\tlogger.info('Starting task execution')\n\t\tlogger.info(\\`Processing address: \\${targetAddress}\\`)\n\n\t\t// Example: Read from blockchain using the public client\n\t\t// const balance = await ctx.client.getBalance({ address: targetAddress })\n\t\t// logger.info(\\`Balance: \\${balance}\\`)\n\t\t//\n\t\t// const blockNumber = await ctx.client.getBlockNumber()\n\t\t// logger.info(\\`Current block: \\${blockNumber}\\`)\n\t\t//\n\t\t// const value = await ctx.client.readContract({\n\t\t// address: targetAddress,\n\t\t// abi: [...],\n\t\t// functionName: 'balanceOf',\n\t\t// args: [someAddress],\n\t\t// })\n\n\t\t// Example: Log warnings and errors\n\t\t// logger.warn('Low balance detected')\n\t\t// logger.error('Failed to fetch data')\n\n\t\t// Example: Return calls to execute\n\t\tlogger.info('Task completed successfully')\n\t\treturn {\n\t\t\tcanExec: true,\n\t\t\tcalls: [\n\t\t\t\t{\n\t\t\t\t\tto: targetAddress,\n\t\t\t\t\tdata: '0x' as const,\n\t\t\t\t},\n\t\t\t],\n\t\t}\n\n\t\t// Example with encodeFunctionData:\n\t\t// const abi = [...] as const\n\t\t// return {\n\t\t// canExec: true,\n\t\t// calls: [\n\t\t// {\n\t\t// to: targetAddress,\n\t\t// data: encodeFunctionData({\n\t\t// abi,\n\t\t// functionName: 'transfer',\n\t\t// args: [recipientAddress, 1000n],\n\t\t// }),\n\t\t// },\n\t\t// ],\n\t\t// }\n\n\t\t// Or return false if conditions not met\n\t\t// return {\n\t\t// canExec: false,\n\t\t// message: 'Conditions not met'\n\t\t// }\n\t},\n})\n`\n\n\t\tawait writeFile(join(taskPath, 'index.ts'), indexTs)\n\n\t\t// Create args.json\n\t\tconst args = {\n\t\t\ttargetAddress: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',\n\t\t}\n\n\t\tawait writeFile(join(taskPath, 'args.json'), JSON.stringify(args, null, 2))\n\n\t\tspinner.stop('Task created successfully!')\n\n\t\toutro(\n\t\t\t`${pc.green('✓')} Task \"${finalTaskName}\" created!\\n\\nNext steps:\\n ${pc.cyan('Edit')} functions/${finalTaskName}/index.ts\\n ${pc.cyan('Update')} functions/${finalTaskName}/args.json\\n ${pc.cyan('thyme run')} ${finalTaskName}`,\n\t\t)\n\t} catch (err) {\n\t\tspinner.stop('Failed to create task')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n}\n","import { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport type { Address } from 'viem'\nimport { createPublicClient, formatEther, http, isAddress } from 'viem'\nimport { checkDeno, runInDeno, type TaskConfig } from '../deno/runner'\nimport { getEnv, loadEnv } from '../utils/env'\nimport {\n\tdiscoverTasks,\n\tgetTaskArgsPath,\n\tgetTaskPath,\n\tisThymeProject,\n\tvalidateTaskName,\n} from '../utils/tasks'\nimport {\n\tclack,\n\terror,\n\tinfo,\n\tintro,\n\tlog,\n\toutro,\n\tpc,\n\tstep,\n\twarn,\n} from '../utils/ui'\n\ninterface RunOptions {\n\tsimulate?: boolean\n}\n\nexport async function runCommand(taskName?: string, options: RunOptions = {}) {\n\tintro('Thyme CLI - Run Task')\n\n\tconst projectRoot = process.cwd()\n\n\t// Load environment variables\n\tloadEnv(projectRoot)\n\n\t// Check if we're in a Thyme project\n\tif (!isThymeProject(projectRoot)) {\n\t\terror('Not in a Thyme project')\n\t\tprocess.exit(1)\n\t}\n\n\t// Check if Deno is installed\n\tconst hasDeno = await checkDeno()\n\tif (!hasDeno) {\n\t\terror('Deno is not installed. Please install Deno: https://deno.land/')\n\t\tprocess.exit(1)\n\t}\n\n\t// Discover tasks if no task name provided\n\tlet finalTaskName = taskName\n\n\t// Validate task name if provided via CLI argument\n\tif (finalTaskName) {\n\t\ttry {\n\t\t\tvalidateTaskName(finalTaskName)\n\t\t} catch (err) {\n\t\t\terror(err instanceof Error ? err.message : String(err))\n\t\t\tprocess.exit(1)\n\t\t}\n\t} else {\n\t\tconst tasks = await discoverTasks(projectRoot)\n\n\t\tif (tasks.length === 0) {\n\t\t\terror('No tasks found. Create one with `thyme new`')\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst selected = await clack.select({\n\t\t\tmessage: 'Select a task to run:',\n\t\t\toptions: tasks.map((task) => ({ value: task, label: task })),\n\t\t})\n\n\t\tif (clack.isCancel(selected)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tfinalTaskName = selected as string\n\t}\n\n\tlet taskPath: string\n\tlet argsPath: string\n\ttry {\n\t\ttaskPath = getTaskPath(projectRoot, finalTaskName)\n\t\targsPath = getTaskArgsPath(projectRoot, finalTaskName)\n\t} catch (err) {\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n\n\t// Check if task exists\n\tif (!existsSync(taskPath)) {\n\t\terror(`Task \"${finalTaskName}\" not found`)\n\t\tprocess.exit(1)\n\t}\n\n\t// Use default config\n\tconst config: TaskConfig = {\n\t\tmemory: 128,\n\t\ttimeout: 30,\n\t\tnetwork: true,\n\t\trpcUrl: getEnv('RPC_URL'),\n\t}\n\n\t// Load args\n\tlet args: unknown = {}\n\tif (existsSync(argsPath)) {\n\t\ttry {\n\t\t\tconst argsData = await readFile(argsPath, 'utf-8')\n\t\t\targs = JSON.parse(argsData)\n\t\t} catch (err) {\n\t\t\twarn(\n\t\t\t\t`Failed to load args.json: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Executing task in Deno sandbox...')\n\n\t// Run task\n\tconst result = await runInDeno(taskPath, args, config)\n\n\tif (!result.success) {\n\t\tspinner.stop('Task execution failed')\n\t\terror(result.error ?? 'Unknown error')\n\t\tif (result.logs.length > 0) {\n\t\t\tstep('Task output:')\n\t\t\tfor (const taskLog of result.logs) {\n\t\t\t\tlog(` ${taskLog}`)\n\t\t\t}\n\t\t}\n\t\tprocess.exit(1)\n\t}\n\n\tspinner.stop('Task executed successfully')\n\n\t// Show logs\n\tif (result.logs.length > 0) {\n\t\tlog('')\n\t\tstep('Task output:')\n\t\tfor (const taskLog of result.logs) {\n\t\t\tlog(` ${taskLog}`)\n\t\t}\n\t}\n\n\t// Show result\n\tif (!result.result) {\n\t\terror('No result returned from task')\n\t\tprocess.exit(1)\n\t}\n\n\tlog('')\n\tif (result.result.canExec) {\n\t\tinfo(\n\t\t\t`${pc.green('✓')} Result: canExec = true (${result.result.calls.length} call(s))`,\n\t\t)\n\n\t\t// Show calls\n\t\tlog('')\n\t\tstep('Calls to execute:')\n\t\tfor (const call of result.result.calls) {\n\t\t\tlog(` ${pc.cyan('→')} to: ${call.to}`)\n\t\t\tlog(` data: ${call.data}`)\n\t\t}\n\n\t\t// Simulate if requested\n\t\tif (options.simulate) {\n\t\t\tlog('')\n\t\t\tawait simulateCalls(result.result.calls)\n\t\t}\n\t} else {\n\t\twarn('Result: canExec = false')\n\t\tinfo(`Message: ${result.result.message}`)\n\t}\n\n\t// Show execution stats\n\tlog('')\n\tif (\n\t\tresult.executionTime !== undefined ||\n\t\tresult.memoryUsed !== undefined ||\n\t\tresult.rpcRequestCount !== undefined\n\t) {\n\t\tstep('Execution stats:')\n\t\tif (result.executionTime !== undefined) {\n\t\t\tlog(` Duration: ${result.executionTime.toFixed(2)}ms`)\n\t\t}\n\t\tif (result.memoryUsed !== undefined) {\n\t\t\tconst memoryMB = (result.memoryUsed / 1024 / 1024).toFixed(2)\n\t\t\tlog(` Memory: ${memoryMB}MB`)\n\t\t}\n\t\tif (result.rpcRequestCount !== undefined) {\n\t\t\tlog(` RPC Requests: ${result.rpcRequestCount}`)\n\t\t}\n\t}\n\n\t// Show simulation tip if task can execute and simulation wasn't run\n\tif (result.result?.canExec && !options.simulate) {\n\t\tlog('')\n\t\tinfo(\n\t\t\t`${pc.dim('💡 Tip: Test calls on-chain with:')} ${pc.cyan(`thyme run ${finalTaskName} --simulate`)}`,\n\t\t)\n\t\toutro('')\n\t} else {\n\t\toutro('')\n\t}\n}\n\nasync function simulateCalls(\n\tcalls: Array<{ to: Address; data: `0x${string}` }>,\n) {\n\tconst rpcUrl = getEnv('RPC_URL')\n\tconst account = getEnv('SIMULATE_ACCOUNT')\n\n\tif (!rpcUrl || !account) {\n\t\twarn('Simulation requires RPC_URL and SIMULATE_ACCOUNT in .env file')\n\t\treturn\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Simulating on-chain...')\n\n\ttry {\n\t\tconst client = createPublicClient({\n\t\t\ttransport: http(rpcUrl),\n\t\t})\n\n\t\t// Get chain info\n\t\tconst chainId = await client.getChainId()\n\t\tconst blockNumber = await client.getBlockNumber()\n\n\t\tspinner.stop('Simulating on-chain...')\n\n\t\tlog('')\n\t\tinfo(`Chain ID: ${chainId}`)\n\t\tinfo(`Block: ${blockNumber}`)\n\t\tinfo(`Account: ${account}`)\n\n\t\t// Validate account address\n\t\tif (!isAddress(account)) {\n\t\t\tspinner.stop('Invalid account address')\n\t\t\tlog('')\n\t\t\terror(`SIMULATE_ACCOUNT is not a valid Ethereum address: ${account}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Simulate all calls at once using viem's simulateCalls\n\t\tconst simulationSpinner = clack.spinner()\n\t\tsimulationSpinner.start('Running simulation...')\n\n\t\tconst { results } = await client.simulateCalls({\n\t\t\taccount: account as Address,\n\t\t\tcalls: calls.map((call) => ({\n\t\t\t\tto: call.to,\n\t\t\t\tdata: call.data,\n\t\t\t})),\n\t\t})\n\n\t\tsimulationSpinner.stop('Simulation complete')\n\n\t\t// Check results for failures\n\t\tconst failedCalls: Array<{\n\t\t\tindex: number\n\t\t\tcall: { to: Address; data: `0x${string}` }\n\t\t\terror?: string\n\t\t}> = []\n\t\tfor (let i = 0; i < results.length; i++) {\n\t\t\tconst result = results[i]\n\t\t\tconst call = calls[i]\n\t\t\tif (!result || !call) continue\n\n\t\t\tif (result.status === 'failure') {\n\t\t\t\tfailedCalls.push({\n\t\t\t\t\tindex: i,\n\t\t\t\t\tcall,\n\t\t\t\t\terror: result.error?.message || 'Unknown error',\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tif (failedCalls.length > 0) {\n\t\t\tlog('')\n\t\t\terror('Some calls would revert:')\n\t\t\tfor (const failed of failedCalls) {\n\t\t\t\terror(\n\t\t\t\t\t` Call ${failed.index + 1} to ${failed.call.to}: ${failed.error}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Get gas price\n\t\tconst gasPrice = await client.getGasPrice()\n\n\t\tclack.log.step('Simulation results:')\n\t\tclack.log.success('All calls would succeed')\n\n\t\t// Show gas usage if available\n\t\tconst totalGas = results.reduce((sum, r) => sum + (r.gasUsed || 0n), 0n)\n\t\tif (totalGas > 0n) {\n\t\t\tclack.log.message(` Total gas: ${totalGas.toString()}`)\n\t\t}\n\n\t\tclack.log.message(` Gas price: ${formatEther(gasPrice)} ETH`)\n\t} catch (err) {\n\t\tspinner.stop('Simulation failed')\n\t\tlog('')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t}\n}\n","import { spawn } from 'node:child_process'\nimport { dirname, resolve } from 'node:path'\nimport type { TaskResult } from '@thyme-sh/sdk'\n\nexport interface TaskConfig {\n\tmemory: number // MB\n\ttimeout: number // seconds\n\tnetwork: boolean\n\trpcUrl?: string // RPC URL for public client\n}\n\nexport interface RunResult {\n\tsuccess: boolean\n\tresult?: TaskResult\n\tlogs: string[]\n\terror?: string\n\texecutionTime?: number // milliseconds\n\tmemoryUsed?: number // bytes\n\trpcRequestCount?: number // number of RPC requests made\n}\n\n/**\n * Escape a string for safe use in JavaScript string literals\n * Prevents command injection attacks\n */\nfunction escapeJsString(str: string): string {\n\treturn str\n\t\t.replace(/\\\\/g, '\\\\\\\\')\n\t\t.replace(/'/g, \"\\\\'\")\n\t\t.replace(/\"/g, '\\\\\"')\n\t\t.replace(/\\n/g, '\\\\n')\n\t\t.replace(/\\r/g, '\\\\r')\n\t\t.replace(/\\t/g, '\\\\t')\n\t\t.replace(/\\0/g, '\\\\0')\n}\n\n/**\n * Sanitize args to prevent prototype pollution and injection\n * Deep clones the object to remove any prototype chain issues\n */\nfunction sanitizeArgs(args: unknown): unknown {\n\tif (args === null || args === undefined) return args\n\tif (typeof args !== 'object') return args\n\n\t// Deep clone via JSON to strip prototypes and non-serializable values\n\ttry {\n\t\treturn JSON.parse(JSON.stringify(args))\n\t} catch {\n\t\treturn {}\n\t}\n}\n\n/**\n * Sanitize error messages to prevent information disclosure\n * Removes sensitive paths and internal details\n */\nfunction sanitizeErrorMessage(error: string): string {\n\t// Remove absolute paths (keep only filename)\n\tlet sanitized = error.replace(/\\/[^\\s:]+\\//g, '.../')\n\n\t// Remove stack traces\n\tsanitized = sanitized.replace(/\\s+at\\s+.+/g, '')\n\n\t// Limit length\n\tif (sanitized.length > 500) {\n\t\tsanitized = `${sanitized.substring(0, 500)}...`\n\t}\n\n\treturn sanitized.trim()\n}\n\n/**\n * Run a task in Deno sandbox - similar to Gelato's w3f test and @deno/sandbox\n * Creates an isolated Deno process with controlled permissions\n */\nexport async function runInDeno(\n\ttaskPath: string,\n\targs: unknown,\n\tconfig: TaskConfig,\n): Promise<RunResult> {\n\tconst taskDir = dirname(resolve(taskPath))\n\tconst absoluteTaskPath = resolve(taskPath)\n\n\t// Escape path for safe JavaScript string interpolation\n\tconst safeTaskPath = escapeJsString(absoluteTaskPath)\n\n\t// Sanitize args to prevent prototype pollution\n\tconst safeArgs = sanitizeArgs(args)\n\n\t// Safely serialize RPC URL\n\tconst safeRpcUrl = config.rpcUrl ? JSON.stringify(config.rpcUrl) : 'undefined'\n\n\tconst denoFlags = ['run', '--no-prompt']\n\n\t// Sandbox permissions - minimal by default, similar to @deno/sandbox\n\tdenoFlags.push(`--allow-read=${taskDir}`) // Only allow reading task directory\n\n\t// Add memory limit if specified\n\tif (config.memory) {\n\t\tdenoFlags.push(`--v8-flags=--max-old-space-size=${config.memory}`)\n\t}\n\n\t// Conditionally allow network (similar to allowNet in @deno/sandbox)\n\tif (config.network) {\n\t\tdenoFlags.push('--allow-net')\n\t}\n\n\t// Execute inline wrapper via stdin (similar to Gelato's approach)\n\tdenoFlags.push('-')\n\n\t// Execution wrapper that loads and runs the task\n\t// Similar to how Gelato's w3f test executes functions\n\tconst execScript = `\nimport task from '${safeTaskPath}';\nimport { createPublicClient, http } from 'npm:viem@2.21.54';\n\n// Logger for local development - prints directly to console\n// (In production, the logger outputs with __THYME_LOG__ prefix for capture)\nclass Logger {\n\tinfo(message) {\n\t\tconsole.log('[INFO]', message);\n\t}\n\t\n\twarn(message) {\n\t\tconsole.log('[WARN]', message);\n\t}\n\t\n\terror(message) {\n\t\tconsole.log('[ERROR]', message);\n\t}\n}\n\n// Create RPC request counter\nlet rpcRequestCount = 0;\n\n// Wrap the http transport to count requests\nconst countingHttp = (url) => {\n\tconst baseTransport = http(url);\n\treturn (config) => {\n\t\tconst transport = baseTransport(config);\n\t\treturn {\n\t\t\t...transport,\n\t\t\trequest: async (params) => {\n\t\t\t\trpcRequestCount++;\n\t\t\t\treturn transport.request(params);\n\t\t\t},\n\t\t};\n\t};\n};\n\n// Create public client for blockchain reads\nconst client = createPublicClient({\n\ttransport: countingHttp(${safeRpcUrl}),\n});\n\nconst context = {\n\targs: ${JSON.stringify(safeArgs)},\n\tclient,\n\tlogger: new Logger(),\n};\n\ntry {\n\t// Track execution time and memory\n\tconst startTime = performance.now();\n\tconst startMemory = Deno.memoryUsage().heapUsed;\n\t\n\tconst result = await task.run(context);\n\t\n\tconst endTime = performance.now();\n\tconst endMemory = Deno.memoryUsage().heapUsed;\n\t\n\tconst executionTime = endTime - startTime;\n\t// Ensure memory measurement is non-negative (GC can cause negative values)\n\tconst memoryUsed = Math.max(0, endMemory - startMemory);\n\t\n\tconsole.log('__THYME_RESULT__' + JSON.stringify(result));\n\tconsole.log('__THYME_STATS__' + JSON.stringify({ executionTime, memoryUsed, rpcRequestCount }));\n} catch (error) {\n\tconsole.error('Task execution error:', error instanceof Error ? error.message : String(error));\n\tDeno.exit(1);\n}\n`\n\n\treturn new Promise((resolve) => {\n\t\tconst proc = spawn('deno', denoFlags, {\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\tcwd: taskDir,\n\t\t})\n\n\t\tlet stdout = ''\n\t\tlet stderr = ''\n\t\tconst logs: string[] = []\n\n\t\t// Write the execution script to stdin\n\t\tproc.stdin?.write(execScript)\n\t\tproc.stdin?.end()\n\n\t\tproc.stdout?.on('data', (data) => {\n\t\t\tstdout += data.toString()\n\t\t})\n\n\t\tproc.stderr?.on('data', (data) => {\n\t\t\tstderr += data.toString()\n\t\t})\n\n\t\tproc.on('close', (code) => {\n\t\t\tif (code !== 0) {\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tlogs,\n\t\t\t\t\terror: sanitizeErrorMessage(\n\t\t\t\t\t\tstderr || `Process exited with code ${code}`,\n\t\t\t\t\t),\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Extract logs, result, and stats from stdout\n\t\t\t\tconst lines = stdout.trim().split('\\n')\n\t\t\t\tlet resultLine: string | undefined\n\t\t\t\tlet statsLine: string | undefined\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith('__THYME_RESULT__')) {\n\t\t\t\t\t\tresultLine = line.substring('__THYME_RESULT__'.length)\n\t\t\t\t\t} else if (line.startsWith('__THYME_STATS__')) {\n\t\t\t\t\t\tstatsLine = line.substring('__THYME_STATS__'.length)\n\t\t\t\t\t} else if (line.trim()) {\n\t\t\t\t\t\tlogs.push(line.trim())\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!resultLine) {\n\t\t\t\t\tthrow new Error('No result found in output')\n\t\t\t\t}\n\n\t\t\t\tconst result = JSON.parse(resultLine) as TaskResult\n\t\t\t\tconst stats = statsLine\n\t\t\t\t\t? JSON.parse(statsLine)\n\t\t\t\t\t: {\n\t\t\t\t\t\t\texecutionTime: undefined,\n\t\t\t\t\t\t\tmemoryUsed: undefined,\n\t\t\t\t\t\t\trpcRequestCount: undefined,\n\t\t\t\t\t\t}\n\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tresult,\n\t\t\t\t\tlogs,\n\t\t\t\t\texecutionTime: stats.executionTime,\n\t\t\t\t\tmemoryUsed: stats.memoryUsed,\n\t\t\t\t\trpcRequestCount: stats.rpcRequestCount,\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tresolve({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tlogs,\n\t\t\t\t\terror: sanitizeErrorMessage(\n\t\t\t\t\t\t`Failed to parse result: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t),\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tproc.on('error', (error) => {\n\t\t\tresolve({\n\t\t\t\tsuccess: false,\n\t\t\t\tlogs,\n\t\t\t\terror: sanitizeErrorMessage(`Failed to spawn Deno: ${error.message}`),\n\t\t\t})\n\t\t})\n\t})\n}\n\n/**\n * Check if Deno is installed\n */\nexport async function checkDeno(): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst proc = spawn('deno', ['--version'], { stdio: 'ignore' })\n\t\tproc.on('close', (code) => resolve(code === 0))\n\t\tproc.on('error', () => resolve(false))\n\t})\n}\n","import { existsSync } from 'node:fs'\nimport { z } from 'zod'\nimport { bundleTask } from '../utils/bundler'\nimport { compressTask } from '../utils/compress'\nimport { getEnv, loadEnv } from '../utils/env'\nimport { extractSchemaFromTask } from '../utils/schema-extractor'\nimport {\n\tdiscoverTasks,\n\tgetTaskPath,\n\tisThymeProject,\n\tvalidateTaskName,\n} from '../utils/tasks'\nimport { clack, error, intro, outro, pc } from '../utils/ui'\n\n// Zod schemas for API response validation\nconst organizationSchema = z.object({\n\tid: z.string(),\n\tname: z.string(),\n\trole: z.string(),\n})\n\nconst verifyResponseSchema = z.object({\n\tuser: z.object({\n\t\tid: z.string(),\n\t\tname: z.string().optional().default(''),\n\t\temail: z.string(),\n\t}),\n\torganizations: z.array(organizationSchema).optional().default([]),\n})\n\nconst uploadResponseSchema = z.object({\n\ttaskId: z.string().optional(),\n})\n\nexport async function uploadCommand(\n\ttaskName?: string,\n\torganizationId?: string,\n) {\n\tintro('Thyme CLI - Upload Task')\n\n\tconst projectRoot = process.cwd()\n\n\t// Load environment variables\n\tloadEnv(projectRoot)\n\n\t// Check if we're in a Thyme project\n\tif (!isThymeProject(projectRoot)) {\n\t\terror('Not in a Thyme project')\n\t\tprocess.exit(1)\n\t}\n\n\t// Check for auth token\n\tconst authToken = getEnv('THYME_AUTH_TOKEN')\n\tif (!authToken) {\n\t\terror('Not authenticated. Run `thyme login` first.')\n\t\tprocess.exit(1)\n\t}\n\n\t// Get API URL (Convex deployment URL)\n\tconst apiUrl = getEnv('THYME_API_URL')\n\tif (!apiUrl) {\n\t\terror(\n\t\t\t'THYME_API_URL is not set. Please set it to your Convex deployment URL in .env',\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\t// Discover tasks if no task name provided\n\tlet finalTaskName = taskName\n\n\t// Validate task name if provided via CLI argument\n\tif (finalTaskName) {\n\t\ttry {\n\t\t\tvalidateTaskName(finalTaskName)\n\t\t} catch (err) {\n\t\t\terror(err instanceof Error ? err.message : String(err))\n\t\t\tprocess.exit(1)\n\t\t}\n\t} else {\n\t\tconst tasks = await discoverTasks(projectRoot)\n\n\t\tif (tasks.length === 0) {\n\t\t\terror('No tasks found. Create one with `thyme new`')\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst selected = await clack.select({\n\t\t\tmessage: 'Select a task to upload:',\n\t\t\toptions: tasks.map((task) => ({ value: task, label: task })),\n\t\t})\n\n\t\tif (clack.isCancel(selected)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tfinalTaskName = selected as string\n\t}\n\n\t// Fetch user's organizations\n\tconst orgSpinner = clack.spinner()\n\torgSpinner.start('Fetching organizations...')\n\n\tlet organizations: z.infer<typeof organizationSchema>[] = []\n\ttry {\n\t\tconst verifyResponse = await fetch(`${apiUrl}/api/auth/verify`, {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${authToken}`,\n\t\t\t},\n\t\t})\n\n\t\tif (!verifyResponse.ok) {\n\t\t\torgSpinner.stop('Failed to fetch organizations')\n\t\t\terror('Failed to authenticate. Please run `thyme login` again.')\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst rawData = await verifyResponse.json()\n\n\t\t// Validate response with Zod\n\t\tconst parseResult = verifyResponseSchema.safeParse(rawData)\n\t\tif (!parseResult.success) {\n\t\t\torgSpinner.stop('Invalid API response')\n\t\t\terror(`API returned unexpected data format: ${parseResult.error.message}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\torganizations = parseResult.data.organizations\n\t\torgSpinner.stop('Organizations loaded')\n\t} catch (err) {\n\t\torgSpinner.stop('Failed to fetch organizations')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n\n\t// Check if user has any organizations\n\tif (organizations.length === 0) {\n\t\terror(\n\t\t\t'You are not a member of any organizations. Please create or join an organization first.',\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\t// Determine organization to upload to\n\tlet selectedOrgId = organizationId\n\n\t// If organization ID was provided, validate it\n\tif (selectedOrgId) {\n\t\tconst orgExists = organizations.find((org) => org.id === selectedOrgId)\n\t\tif (!orgExists) {\n\t\t\terror(\n\t\t\t\t`Organization with ID \"${selectedOrgId}\" not found or you don't have access to it.`,\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\t} else {\n\t\t// Prompt user to select an organization\n\t\tconst selected = await clack.select({\n\t\t\tmessage: 'Select an organization to upload to:',\n\t\t\toptions: organizations.map((org) => ({\n\t\t\t\tvalue: org.id,\n\t\t\t\tlabel: `${org.name} ${pc.dim(`(${org.role})`)}`,\n\t\t\t})),\n\t\t})\n\n\t\tif (clack.isCancel(selected)) {\n\t\t\tclack.cancel('Operation cancelled')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tselectedOrgId = selected as string\n\t}\n\n\tlet taskPath: string\n\ttry {\n\t\ttaskPath = getTaskPath(projectRoot, finalTaskName)\n\t} catch (err) {\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n\n\t// Check if task exists\n\tif (!existsSync(taskPath)) {\n\t\terror(`Task \"${finalTaskName}\" not found`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst spinner = clack.spinner()\n\tspinner.start('Bundling task...')\n\n\ttry {\n\t\t// Bundle task code with all dependencies\n\t\tconst { source, bundle } = await bundleTask(taskPath)\n\n\t\tspinner.message('Extracting schema...')\n\n\t\t// Extract schema from source code\n\t\tconst schema = extractSchemaFromTask(source)\n\n\t\tspinner.message('Compressing files...')\n\n\t\t// Compress source and bundle into ZIP\n\t\tconst { zipBuffer, checksum } = compressTask(source, bundle)\n\n\t\tspinner.message('Uploading to cloud...')\n\n\t\t// Create form data\n\t\tconst formData = new FormData()\n\n\t\t// Add metadata\n\t\tformData.append(\n\t\t\t'data',\n\t\t\tJSON.stringify({\n\t\t\t\torganizationId: selectedOrgId as string,\n\t\t\t\tcheckSum: checksum,\n\t\t\t\tschema: schema || undefined,\n\t\t\t}),\n\t\t)\n\n\t\t// Add ZIP blob\n\t\tformData.append('blob', new Blob([zipBuffer]), 'task.zip')\n\n\t\t// Upload to API\n\t\tconst response = await fetch(`${apiUrl}/api/task/upload`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${authToken}`,\n\t\t\t},\n\t\t\tbody: formData,\n\t\t})\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text()\n\t\t\tthrow new Error(`Upload failed: ${errorText}`)\n\t\t}\n\n\t\tconst rawResult = await response.json()\n\n\t\t// Validate response with Zod\n\t\tconst resultParseResult = uploadResponseSchema.safeParse(rawResult)\n\t\tif (!resultParseResult.success) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid upload response: ${resultParseResult.error.message}`,\n\t\t\t)\n\t\t}\n\n\t\tconst result = resultParseResult.data\n\n\t\tspinner.stop('Task uploaded successfully!')\n\n\t\tconst selectedOrg = organizations.find((org) => org.id === selectedOrgId)\n\n\t\tclack.log.message('')\n\t\tclack.log.success('Upload details:')\n\t\tclack.log.message(` ${pc.dim('Task:')} ${pc.cyan(finalTaskName)}`)\n\t\tclack.log.message(\n\t\t\t` ${pc.dim('Organization:')} ${pc.cyan(selectedOrg?.name || 'Unknown')}`,\n\t\t)\n\t\tclack.log.message(\n\t\t\t` ${pc.dim('Size:')} ${(zipBuffer.length / 1024).toFixed(2)} KB`,\n\t\t)\n\t\tclack.log.message(` ${pc.dim('Checksum:')} ${checksum.slice(0, 16)}...`)\n\t\tif (result.taskId) {\n\t\t\tclack.log.message(` ${pc.dim('Task ID:')} ${pc.green(result.taskId)}`)\n\t\t}\n\n\t\toutro(\n\t\t\t`${pc.green('✓')} Task uploaded!\\n\\n` +\n\t\t\t\t`Configure triggers in the dashboard: ${pc.cyan('https://thyme.sh/dashboard')}`,\n\t\t)\n\t} catch (err) {\n\t\tspinner.stop('Upload failed')\n\t\terror(err instanceof Error ? err.message : String(err))\n\t\tprocess.exit(1)\n\t}\n}\n","import { readFile } from 'node:fs/promises'\nimport { build } from 'esbuild'\n\nexport interface BundleResult {\n\tsource: string\n\tbundle: string\n}\n\n/**\n * Bundle task code with all dependencies using esbuild\n * Target: ESM format for Deno compatibility\n */\nexport async function bundleTask(taskPath: string): Promise<BundleResult> {\n\t// Read original source\n\tconst source = await readFile(taskPath, 'utf-8')\n\n\t// Node.js built-in modules that should not be bundled\n\tconst nodeBuiltins = [\n\t\t'assert',\n\t\t'buffer',\n\t\t'child_process',\n\t\t'cluster',\n\t\t'crypto',\n\t\t'dgram',\n\t\t'dns',\n\t\t'events',\n\t\t'fs',\n\t\t'http',\n\t\t'http2',\n\t\t'https',\n\t\t'net',\n\t\t'os',\n\t\t'path',\n\t\t'perf_hooks',\n\t\t'process',\n\t\t'querystring',\n\t\t'readline',\n\t\t'stream',\n\t\t'string_decoder',\n\t\t'timers',\n\t\t'tls',\n\t\t'tty',\n\t\t'url',\n\t\t'util',\n\t\t'v8',\n\t\t'vm',\n\t\t'zlib',\n\t\t// Node: prefix versions\n\t\t'node:assert',\n\t\t'node:buffer',\n\t\t'node:child_process',\n\t\t'node:cluster',\n\t\t'node:crypto',\n\t\t'node:dgram',\n\t\t'node:dns',\n\t\t'node:events',\n\t\t'node:fs',\n\t\t'node:http',\n\t\t'node:http2',\n\t\t'node:https',\n\t\t'node:net',\n\t\t'node:os',\n\t\t'node:path',\n\t\t'node:perf_hooks',\n\t\t'node:process',\n\t\t'node:querystring',\n\t\t'node:readline',\n\t\t'node:stream',\n\t\t'node:string_decoder',\n\t\t'node:timers',\n\t\t'node:tls',\n\t\t'node:tty',\n\t\t'node:url',\n\t\t'node:util',\n\t\t'node:v8',\n\t\t'node:vm',\n\t\t'node:zlib',\n\t]\n\n\t// Bundle with esbuild\n\tconst result = await build({\n\t\tentryPoints: [taskPath],\n\t\tbundle: true,\n\t\tformat: 'esm',\n\t\tplatform: 'neutral',\n\t\ttarget: 'esnext',\n\t\twrite: false,\n\t\ttreeShaking: true,\n\t\tminify: false, // Keep readable for debugging\n\t\tsourcemap: false,\n\t\texternal: nodeBuiltins, // Don't bundle Node.js built-ins\n\t\tlogLevel: 'silent',\n\t})\n\n\tif (result.outputFiles.length === 0) {\n\t\tthrow new Error('No output from bundler')\n\t}\n\n\tconst outputFile = result.outputFiles[0]\n\tif (!outputFile) {\n\t\tthrow new Error('No output from bundler')\n\t}\n\n\tconst bundle = outputFile.text\n\n\treturn {\n\t\tsource,\n\t\tbundle,\n\t}\n}\n","import { compressTask as sdkCompressTask } from '@thyme-sh/sdk'\n\nexport interface CompressResult {\n\tzipBuffer: Buffer\n\tchecksum: string\n}\n\n/**\n * Compress source and bundle into a ZIP archive\n * Uses SDK's compression function with fflate\n */\nexport function compressTask(source: string, bundle: string): CompressResult {\n\tconst { zipBuffer, checksum } = sdkCompressTask(source, bundle)\n\n\t// Convert Uint8Array to Buffer for Node.js\n\treturn {\n\t\tzipBuffer: Buffer.from(zipBuffer),\n\t\tchecksum,\n\t}\n}\n","/**\n * Extract Zod schema from task code and convert to JSON Schema\n * This allows the frontend to generate forms for task arguments\n */\nexport function extractSchemaFromTask(taskCode: string): string | null {\n\ttry {\n\t\t// For now, return a simple extraction by parsing the code\n\t\t// This is a simplified version - in production you might want to use a proper parser\n\t\tconst schemaMatch = taskCode.match(/schema:\\s*z\\.object\\(\\{([^}]+)\\}\\)/)\n\n\t\tif (!schemaMatch || !schemaMatch[1]) {\n\t\t\treturn null\n\t\t}\n\n\t\t// Parse the schema fields\n\t\tconst schemaContent = schemaMatch[1]\n\t\tconst fields: Record<string, unknown> = {}\n\n\t\t// Simple regex to extract field definitions\n\t\tconst fieldMatches = schemaContent.matchAll(/(\\w+):\\s*z\\.(\\w+)\\(\\)/g)\n\n\t\tfor (const match of fieldMatches) {\n\t\t\tconst [, fieldName, fieldType] = match\n\t\t\tif (fieldName && fieldType) {\n\t\t\t\t// Convert Zod types to JSON Schema types\n\t\t\t\tlet jsonType = 'string'\n\t\t\t\tswitch (fieldType) {\n\t\t\t\t\tcase 'string':\n\t\t\t\t\t\tjsonType = 'string'\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'number':\n\t\t\t\t\t\tjsonType = 'number'\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'boolean':\n\t\t\t\t\t\tjsonType = 'boolean'\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'address':\n\t\t\t\t\t\tjsonType = 'string'\n\t\t\t\t\t\tbreak\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tjsonType = 'string'\n\t\t\t\t}\n\n\t\t\t\tfields[fieldName] = {\n\t\t\t\t\ttype: jsonType,\n\t\t\t\t\t...(fieldType === 'address' && {\n\t\t\t\t\t\tpattern: '^0x[a-fA-F0-9]{40}$',\n\t\t\t\t\t\tdescription: 'Ethereum address',\n\t\t\t\t\t}),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Object.keys(fields).length === 0) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst jsonSchema = {\n\t\t\ttype: 'object',\n\t\t\tproperties: fields,\n\t\t\trequired: Object.keys(fields),\n\t\t}\n\n\t\treturn JSON.stringify(jsonSchema)\n\t} catch (err) {\n\t\tconsole.error('Error extracting schema:', err)\n\t\treturn null\n\t}\n}\n"],"mappings":"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","names":["dirname","join","intro","outro","log","intro","outro","existsSync","join","intro","outro","existsSync","writeFile","join","existsSync","join","intro","join","existsSync","writeFile","outro","existsSync","mkdir","writeFile","join","intro","join","existsSync","mkdir","writeFile","outro","existsSync","readFile","resolve","error","config","intro","existsSync","config","readFile","log","outro","existsSync","z","readFile","z","verifyResponseSchema","intro","existsSync","outro","__dirname","dirname","join"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thyme-sh/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "CLI for developing and deploying Thyme tasks",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/bnhpio/thyme-sdk.git",
|
|
8
|
+
"directory": "packages/cli"
|
|
9
|
+
},
|
|
5
10
|
"type": "module",
|
|
6
11
|
"bin": {
|
|
7
12
|
"thyme": "./dist/index.js"
|
|
@@ -22,9 +27,13 @@
|
|
|
22
27
|
],
|
|
23
28
|
"author": "",
|
|
24
29
|
"license": "MIT",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public",
|
|
32
|
+
"provenance": true
|
|
33
|
+
},
|
|
25
34
|
"dependencies": {
|
|
26
35
|
"@clack/prompts": "^0.8.2",
|
|
27
|
-
"@thyme-sh/sdk": "^0.
|
|
36
|
+
"@thyme-sh/sdk": "^0.3.1",
|
|
28
37
|
"commander": "^12.1.0",
|
|
29
38
|
"dotenv": "^16.4.7",
|
|
30
39
|
"esbuild": "^0.24.2",
|