veryfront 0.1.21 → 0.1.23
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/bin/veryfront.js +24 -11
- package/esm/cli/commands/task/command-help.d.ts +3 -0
- package/esm/cli/commands/task/command-help.d.ts.map +1 -0
- package/esm/cli/commands/task/command-help.js +20 -0
- package/esm/cli/commands/task/command.d.ts +5 -0
- package/esm/cli/commands/task/command.d.ts.map +1 -0
- package/esm/cli/commands/task/command.js +79 -0
- package/esm/cli/commands/task/handler.d.ts +24 -0
- package/esm/cli/commands/task/handler.d.ts.map +1 -0
- package/esm/cli/commands/task/handler.js +17 -0
- package/esm/cli/help/command-definitions.d.ts.map +1 -1
- package/esm/cli/help/command-definitions.js +2 -0
- package/esm/cli/router.d.ts.map +1 -1
- package/esm/cli/router.js +2 -0
- package/esm/deno.d.ts +1 -0
- package/esm/deno.js +2 -1
- package/esm/src/discovery/discovery-engine.d.ts.map +1 -1
- package/esm/src/discovery/discovery-engine.js +6 -1
- package/esm/src/discovery/handlers/index.d.ts +1 -0
- package/esm/src/discovery/handlers/index.d.ts.map +1 -1
- package/esm/src/discovery/handlers/index.js +1 -0
- package/esm/src/discovery/handlers/task-handler.d.ts +7 -0
- package/esm/src/discovery/handlers/task-handler.d.ts.map +1 -0
- package/esm/src/discovery/handlers/task-handler.js +19 -0
- package/esm/src/discovery/types.d.ts +3 -0
- package/esm/src/discovery/types.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts +0 -2
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/adapter.js +3 -7
- package/esm/src/studio/bridge-template.d.ts.map +1 -1
- package/esm/src/studio/bridge-template.js +3 -2
- package/esm/src/task/discovery.d.ts +83 -0
- package/esm/src/task/discovery.d.ts.map +1 -0
- package/esm/src/task/discovery.js +149 -0
- package/esm/src/task/runner.d.ts +34 -0
- package/esm/src/task/runner.d.ts.map +1 -0
- package/esm/src/task/runner.js +45 -0
- package/esm/src/task/types.d.ts +34 -0
- package/esm/src/task/types.d.ts.map +1 -0
- package/esm/src/task/types.js +16 -0
- package/esm/src/transforms/mdx/esm-module-loader/jsx/runtime-loader.js +1 -1
- package/esm/src/workflow/claude-code/tool.d.ts +5 -5
- package/package.json +2 -2
- package/src/cli/commands/task/command-help.ts +22 -0
- package/src/cli/commands/task/command.ts +98 -0
- package/src/cli/commands/task/handler.ts +23 -0
- package/src/cli/help/command-definitions.ts +2 -0
- package/src/cli/router.ts +2 -0
- package/src/deno.js +2 -1
- package/src/src/discovery/discovery-engine.ts +7 -0
- package/src/src/discovery/handlers/index.ts +1 -0
- package/src/src/discovery/handlers/task-handler.ts +23 -0
- package/src/src/discovery/types.ts +3 -0
- package/src/src/platform/adapters/fs/veryfront/adapter.ts +3 -7
- package/src/src/studio/bridge-template.ts +3 -2
- package/src/src/task/discovery.ts +228 -0
- package/src/src/task/runner.ts +94 -0
- package/src/src/task/types.ts +40 -0
- package/src/src/transforms/mdx/esm-module-loader/jsx/runtime-loader.ts +1 -1
- package/esm/deps/esm.sh/react@19.1.1/jsx-dev-runtime.d.ts +0 -2
- package/esm/deps/esm.sh/react@19.1.1/jsx-dev-runtime.d.ts.map +0 -1
- package/esm/deps/esm.sh/react@19.1.1/jsx-dev-runtime.js +0 -3
- package/src/deps/esm.sh/@types/react@19.1.17/global.d.ts +0 -165
- package/src/deps/esm.sh/@types/react@19.1.17/index.d.ts +0 -4267
- package/src/deps/esm.sh/@types/react@19.1.17/jsx-dev-runtime.d.ts +0 -45
- package/src/deps/esm.sh/csstype@3.1.3/index.d.ts +0 -21297
- package/src/deps/esm.sh/react@19.1.1/jsx-dev-runtime.d.ts +0 -45
- package/src/deps/esm.sh/react@19.1.1/jsx-dev-runtime.js +0 -3
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Discovery
|
|
3
|
+
*
|
|
4
|
+
* Discovers task definitions from user's project `tasks/` directory.
|
|
5
|
+
* Follows the same patterns as workflow-discovery.ts.
|
|
6
|
+
*
|
|
7
|
+
* Scans:
|
|
8
|
+
* - tasks/*.ts - task definition files
|
|
9
|
+
* - tasks/**\/*.ts - nested task files
|
|
10
|
+
*
|
|
11
|
+
* Task files should export a task definition:
|
|
12
|
+
* ```typescript
|
|
13
|
+
* export default {
|
|
14
|
+
* name: "Sync external data",
|
|
15
|
+
* run: async (ctx) => {
|
|
16
|
+
* console.log("Syncing data...")
|
|
17
|
+
* return { synced: 42 }
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
import { join } from "../../deps/jsr.io/@std/path/1.1.4/mod.js";
|
|
23
|
+
import { logger as baseLogger } from "../utils/index.js";
|
|
24
|
+
import { collectFiles } from "../utils/file-discovery.js";
|
|
25
|
+
import { loadHandlerModule } from "../routing/api/module-loader/loader.js";
|
|
26
|
+
import { isTaskDefinition } from "./types.js";
|
|
27
|
+
const logger = baseLogger.component("task-discovery");
|
|
28
|
+
/**
|
|
29
|
+
* Derive task ID from file path (e.g., "tasks/sync-data.ts" → "sync-data")
|
|
30
|
+
*/
|
|
31
|
+
export function deriveTaskId(filePath, tasksDir) {
|
|
32
|
+
// Remove the tasks dir prefix and extension
|
|
33
|
+
let relative = filePath;
|
|
34
|
+
const dirPrefix = tasksDir.endsWith("/") ? tasksDir : `${tasksDir}/`;
|
|
35
|
+
if (relative.startsWith(dirPrefix)) {
|
|
36
|
+
relative = relative.slice(dirPrefix.length);
|
|
37
|
+
}
|
|
38
|
+
// Remove extension
|
|
39
|
+
return relative.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Discover all tasks in a project
|
|
43
|
+
*/
|
|
44
|
+
export async function discoverTasks(options) {
|
|
45
|
+
const { projectDir, adapter, config, tasksDir = "tasks", debug = false, } = options;
|
|
46
|
+
const tasks = [];
|
|
47
|
+
const errors = [];
|
|
48
|
+
const fsType = config?.fs?.type ?? "local";
|
|
49
|
+
const useRelativePaths = fsType === "github" || fsType === "veryfront-api";
|
|
50
|
+
const baseDir = useRelativePaths ? tasksDir : join(projectDir, tasksDir);
|
|
51
|
+
if (debug) {
|
|
52
|
+
logger.info(`Scanning ${baseDir} for tasks`);
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const dirExists = await adapter.fs.exists(baseDir);
|
|
56
|
+
if (!dirExists) {
|
|
57
|
+
if (debug) {
|
|
58
|
+
logger.info(`No tasks directory found at ${baseDir}`);
|
|
59
|
+
}
|
|
60
|
+
return { tasks, errors };
|
|
61
|
+
}
|
|
62
|
+
const files = await collectFiles({
|
|
63
|
+
baseDir,
|
|
64
|
+
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
65
|
+
recursive: true,
|
|
66
|
+
ignorePatterns: ["node_modules", ".git", "__tests__", "*.test.*", "*.spec.*"],
|
|
67
|
+
adapter,
|
|
68
|
+
});
|
|
69
|
+
if (debug) {
|
|
70
|
+
logger.info(`Found ${files.length} potential task files`);
|
|
71
|
+
}
|
|
72
|
+
for (const file of files) {
|
|
73
|
+
try {
|
|
74
|
+
const module = await loadHandlerModule({
|
|
75
|
+
projectDir,
|
|
76
|
+
modulePath: file.path,
|
|
77
|
+
adapter,
|
|
78
|
+
config,
|
|
79
|
+
});
|
|
80
|
+
if (!module)
|
|
81
|
+
continue;
|
|
82
|
+
// Prefer default export (aligned with discovery-engine behaviour)
|
|
83
|
+
const defaultExport = module.default;
|
|
84
|
+
if (isTaskDefinition(defaultExport)) {
|
|
85
|
+
const id = deriveTaskId(file.path, baseDir);
|
|
86
|
+
tasks.push({
|
|
87
|
+
id,
|
|
88
|
+
name: defaultExport.name || id,
|
|
89
|
+
filePath: file.path,
|
|
90
|
+
exportName: "default",
|
|
91
|
+
definition: defaultExport,
|
|
92
|
+
});
|
|
93
|
+
if (debug) {
|
|
94
|
+
logger.info(`Found task "${id}" in ${file.path} (export: default)`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Fallback: check named exports
|
|
99
|
+
for (const [exportName, value] of Object.entries(module)) {
|
|
100
|
+
if (exportName === "default")
|
|
101
|
+
continue;
|
|
102
|
+
if (isTaskDefinition(value)) {
|
|
103
|
+
const id = deriveTaskId(file.path, baseDir);
|
|
104
|
+
tasks.push({
|
|
105
|
+
id,
|
|
106
|
+
name: value.name || id,
|
|
107
|
+
filePath: file.path,
|
|
108
|
+
exportName,
|
|
109
|
+
definition: value,
|
|
110
|
+
});
|
|
111
|
+
if (debug) {
|
|
112
|
+
logger.info(`Found task "${id}" in ${file.path} (export: ${exportName})`);
|
|
113
|
+
}
|
|
114
|
+
break; // Only take the first valid named export per file
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
121
|
+
errors.push({ filePath: file.path, error: errorMsg });
|
|
122
|
+
if (debug) {
|
|
123
|
+
logger.warn(`Failed to load ${file.path}: ${errorMsg}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (debug) {
|
|
128
|
+
logger.info(`Discovered ${tasks.length} tasks`);
|
|
129
|
+
}
|
|
130
|
+
return { tasks, errors };
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
134
|
+
logger.error(`Task discovery failed: ${errorMsg}`);
|
|
135
|
+
errors.push({ filePath: baseDir, error: errorMsg });
|
|
136
|
+
return { tasks, errors };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Find a specific task by ID
|
|
141
|
+
*
|
|
142
|
+
* TODO: Optimise by short-circuiting discovery once the target task is found
|
|
143
|
+
* instead of discovering all tasks first. This is consistent with the workflow
|
|
144
|
+
* pattern but could be improved for large projects with many task files.
|
|
145
|
+
*/
|
|
146
|
+
export async function findTaskById(taskId, options) {
|
|
147
|
+
const { tasks } = await discoverTasks(options);
|
|
148
|
+
return tasks.find((t) => t.id === taskId) ?? null;
|
|
149
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DiscoveredTask } from "./discovery.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for running a task
|
|
4
|
+
*/
|
|
5
|
+
export interface RunTaskOptions {
|
|
6
|
+
/** The discovered task to run */
|
|
7
|
+
task: DiscoveredTask;
|
|
8
|
+
/** Additional config to pass to the task */
|
|
9
|
+
config?: Record<string, unknown>;
|
|
10
|
+
/** Project ID (for cloud context) */
|
|
11
|
+
projectId?: string;
|
|
12
|
+
/** If set, only these env var names are passed to the task. */
|
|
13
|
+
envAllowlist?: string[];
|
|
14
|
+
/** Enable debug logging */
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Result of running a task
|
|
19
|
+
*/
|
|
20
|
+
export interface TaskRunResult {
|
|
21
|
+
/** Whether the task completed successfully */
|
|
22
|
+
success: boolean;
|
|
23
|
+
/** Return value from the task's run() */
|
|
24
|
+
result?: unknown;
|
|
25
|
+
/** Error if the task failed */
|
|
26
|
+
error?: string;
|
|
27
|
+
/** Execution duration in milliseconds */
|
|
28
|
+
durationMs: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Run a task with the given options
|
|
32
|
+
*/
|
|
33
|
+
export declare function runTask(options: RunTaskOptions): Promise<TaskRunResult>;
|
|
34
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/src/task/runner.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKrD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,IAAI,EAAE,cAAc,CAAC;IAErB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEjC,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IAEjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAsC7E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Runner
|
|
3
|
+
*
|
|
4
|
+
* Executes a discovered task by calling its run() function
|
|
5
|
+
* with the appropriate context.
|
|
6
|
+
*/
|
|
7
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
8
|
+
import { logger as baseLogger } from "../utils/index.js";
|
|
9
|
+
const logger = baseLogger.component("task-runner");
|
|
10
|
+
/**
|
|
11
|
+
* Run a task with the given options
|
|
12
|
+
*/
|
|
13
|
+
export async function runTask(options) {
|
|
14
|
+
const { task, config = {}, projectId, envAllowlist, debug = false } = options;
|
|
15
|
+
const start = Date.now();
|
|
16
|
+
if (debug) {
|
|
17
|
+
logger.info(`Running task "${task.id}" (${task.name})`);
|
|
18
|
+
}
|
|
19
|
+
const env = { ...dntShim.Deno.env.toObject() };
|
|
20
|
+
if (envAllowlist) {
|
|
21
|
+
for (const k of Object.keys(env)) {
|
|
22
|
+
if (!envAllowlist.includes(k))
|
|
23
|
+
delete env[k];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const ctx = {
|
|
27
|
+
env,
|
|
28
|
+
config,
|
|
29
|
+
projectId,
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
const result = await task.definition.run(ctx);
|
|
33
|
+
const durationMs = Date.now() - start;
|
|
34
|
+
if (debug) {
|
|
35
|
+
logger.info(`Task "${task.id}" completed in ${durationMs}ms`);
|
|
36
|
+
}
|
|
37
|
+
return { success: true, result, durationMs };
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const durationMs = Date.now() - start;
|
|
41
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
42
|
+
logger.error(`Task "${task.id}" failed: ${errorMsg}`);
|
|
43
|
+
return { success: false, error: errorMsg, durationMs };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the task execution system.
|
|
5
|
+
* Tasks are user-defined functions in `tasks/` that can run
|
|
6
|
+
* locally via `veryfront task <name>` or in the cloud as Jobs/CronJobs.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Context passed to task run() function
|
|
10
|
+
*/
|
|
11
|
+
export interface TaskContext {
|
|
12
|
+
/** Environment variables */
|
|
13
|
+
env: Record<string, string>;
|
|
14
|
+
/** Job config (when run as a cloud job) */
|
|
15
|
+
config: Record<string, unknown>;
|
|
16
|
+
/** Project ID (when run as a cloud job) */
|
|
17
|
+
projectId?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Task definition exported from a tasks/ file
|
|
21
|
+
*/
|
|
22
|
+
export interface TaskDefinition {
|
|
23
|
+
/** Human-readable name */
|
|
24
|
+
name?: string;
|
|
25
|
+
/** Task description */
|
|
26
|
+
description?: string;
|
|
27
|
+
/** The function to execute */
|
|
28
|
+
run: (ctx: TaskContext) => Promise<unknown> | unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Type guard: checks if a value looks like a TaskDefinition
|
|
32
|
+
*/
|
|
33
|
+
export declare function isTaskDefinition(value: unknown): value is TaskDefinition;
|
|
34
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/src/task/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,4BAA4B;IAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACvD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAIxE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the task execution system.
|
|
5
|
+
* Tasks are user-defined functions in `tasks/` that can run
|
|
6
|
+
* locally via `veryfront task <name>` or in the cloud as Jobs/CronJobs.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Type guard: checks if a value looks like a TaskDefinition
|
|
10
|
+
*/
|
|
11
|
+
export function isTaskDefinition(value) {
|
|
12
|
+
if (!value || typeof value !== "object")
|
|
13
|
+
return false;
|
|
14
|
+
const obj = value;
|
|
15
|
+
return typeof obj.run === "function";
|
|
16
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export async function loadJSXRuntime() {
|
|
2
2
|
// deno-lint-ignore no-explicit-any
|
|
3
|
-
const runtime = (await import("
|
|
3
|
+
const runtime = (await import("react/jsx-dev-runtime"));
|
|
4
4
|
return {
|
|
5
5
|
Fragment: runtime.Fragment,
|
|
6
6
|
jsx: runtime.jsx ?? runtime.jsxDEV,
|
|
@@ -24,8 +24,8 @@ declare const claudeCodeInputSchema: z.ZodObject<{
|
|
|
24
24
|
system: z.ZodOptional<z.ZodString>;
|
|
25
25
|
}, "strip", z.ZodTypeAny, {
|
|
26
26
|
mode: "code" | "custom" | "full" | "analysis";
|
|
27
|
-
maxTurns: number;
|
|
28
27
|
task: string;
|
|
28
|
+
maxTurns: number;
|
|
29
29
|
context?: Record<string, unknown> | undefined;
|
|
30
30
|
files?: string[] | undefined;
|
|
31
31
|
system?: string | undefined;
|
|
@@ -77,8 +77,8 @@ export declare function createClaudeCodeTool(options?: {
|
|
|
77
77
|
/** Code review tool (analysis mode, read-only) */
|
|
78
78
|
export declare const codeReviewTool: Tool<{
|
|
79
79
|
mode: "code" | "custom" | "full" | "analysis";
|
|
80
|
-
maxTurns: number;
|
|
81
80
|
task: string;
|
|
81
|
+
maxTurns: number;
|
|
82
82
|
context?: Record<string, unknown> | undefined;
|
|
83
83
|
files?: string[] | undefined;
|
|
84
84
|
system?: string | undefined;
|
|
@@ -86,8 +86,8 @@ export declare const codeReviewTool: Tool<{
|
|
|
86
86
|
/** Bug fix tool (code mode) */
|
|
87
87
|
export declare const bugFixTool: Tool<{
|
|
88
88
|
mode: "code" | "custom" | "full" | "analysis";
|
|
89
|
-
maxTurns: number;
|
|
90
89
|
task: string;
|
|
90
|
+
maxTurns: number;
|
|
91
91
|
context?: Record<string, unknown> | undefined;
|
|
92
92
|
files?: string[] | undefined;
|
|
93
93
|
system?: string | undefined;
|
|
@@ -95,8 +95,8 @@ export declare const bugFixTool: Tool<{
|
|
|
95
95
|
/** Refactoring tool (code mode) */
|
|
96
96
|
export declare const refactorTool: Tool<{
|
|
97
97
|
mode: "code" | "custom" | "full" | "analysis";
|
|
98
|
-
maxTurns: number;
|
|
99
98
|
task: string;
|
|
99
|
+
maxTurns: number;
|
|
100
100
|
context?: Record<string, unknown> | undefined;
|
|
101
101
|
files?: string[] | undefined;
|
|
102
102
|
system?: string | undefined;
|
|
@@ -104,8 +104,8 @@ export declare const refactorTool: Tool<{
|
|
|
104
104
|
/** Documentation tool (code mode) */
|
|
105
105
|
export declare const docsTool: Tool<{
|
|
106
106
|
mode: "code" | "custom" | "full" | "analysis";
|
|
107
|
-
maxTurns: number;
|
|
108
107
|
task: string;
|
|
108
|
+
maxTurns: number;
|
|
109
109
|
context?: Record<string, unknown> | undefined;
|
|
110
110
|
files?: string[] | undefined;
|
|
111
111
|
system?: string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "The simplest way to build AI-powered apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"type": "git",
|
|
20
20
|
"url": "git+https://github.com/veryfront/veryfront.git"
|
|
21
21
|
},
|
|
22
|
-
"license": "
|
|
22
|
+
"license": "Apache-2.0",
|
|
23
23
|
"bugs": {
|
|
24
24
|
"url": "https://github.com/veryfront/veryfront/issues"
|
|
25
25
|
},
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CommandHelp } from "../../help/types.js";
|
|
2
|
+
|
|
3
|
+
export const taskHelp: CommandHelp = {
|
|
4
|
+
name: "task",
|
|
5
|
+
description: "Run a task from the tasks/ directory",
|
|
6
|
+
usage: "veryfront task <name> [options]",
|
|
7
|
+
options: [
|
|
8
|
+
{
|
|
9
|
+
flag: "--config <json>",
|
|
10
|
+
description: "JSON config to pass to the task's ctx.config",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
flag: "--debug",
|
|
14
|
+
description: "Enable debug logging",
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
examples: [
|
|
18
|
+
"veryfront task sync-data",
|
|
19
|
+
'veryfront task send-report --config \'{"to": "team@example.com"}\'',
|
|
20
|
+
"veryfront task cleanup --debug",
|
|
21
|
+
],
|
|
22
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task command - Discover and run a task from the tasks/ directory
|
|
3
|
+
*
|
|
4
|
+
* Finds the specified task file, imports it, and calls its run() function
|
|
5
|
+
* with a local execution context.
|
|
6
|
+
*/
|
|
7
|
+
import * as dntShim from "../../../_dnt.shims.js";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
import { cliLogger } from "../../utils/index.js";
|
|
11
|
+
import { exitProcess } from "../../utils/index.js";
|
|
12
|
+
import type { TaskArgs } from "./handler.js";
|
|
13
|
+
|
|
14
|
+
export interface TaskOptions extends TaskArgs {}
|
|
15
|
+
|
|
16
|
+
export async function taskCommand(options: TaskOptions): Promise<void> {
|
|
17
|
+
const { getAdapter } = await import(
|
|
18
|
+
"../../../src/platform/adapters/detect.js"
|
|
19
|
+
);
|
|
20
|
+
const { discoverTasks } = await import(
|
|
21
|
+
"../../../src/task/discovery.js"
|
|
22
|
+
);
|
|
23
|
+
const { runTask } = await import(
|
|
24
|
+
"../../../src/task/runner.js"
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const taskName = options.name;
|
|
28
|
+
if (!taskName) {
|
|
29
|
+
cliLogger.error("Task name is required. Usage: veryfront task <name>");
|
|
30
|
+
exitProcess(1);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const projectDir = dntShim.Deno.cwd();
|
|
35
|
+
const adapter = await getAdapter();
|
|
36
|
+
|
|
37
|
+
cliLogger.info(`Discovering tasks in ${projectDir}/tasks/...`);
|
|
38
|
+
|
|
39
|
+
const { tasks, errors } = await discoverTasks({
|
|
40
|
+
projectDir,
|
|
41
|
+
adapter,
|
|
42
|
+
debug: options.debug,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (errors.length > 0 && options.debug) {
|
|
46
|
+
for (const err of errors) {
|
|
47
|
+
cliLogger.warn(` Warning: ${err.filePath}: ${err.error}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const task = tasks.find((t) => t.id === taskName);
|
|
52
|
+
if (!task) {
|
|
53
|
+
cliLogger.error(`Task "${taskName}" not found.`);
|
|
54
|
+
if (tasks.length > 0) {
|
|
55
|
+
cliLogger.info("Available tasks:");
|
|
56
|
+
for (const t of tasks) {
|
|
57
|
+
cliLogger.info(` - ${t.id}${t.name !== t.id ? ` (${t.name})` : ""}`);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
cliLogger.info("No tasks found. Create a task file in tasks/ directory:");
|
|
61
|
+
cliLogger.info(" tasks/my-task.ts");
|
|
62
|
+
}
|
|
63
|
+
exitProcess(1);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Parse config JSON if provided
|
|
68
|
+
let config: Record<string, unknown> = {};
|
|
69
|
+
if (options.config) {
|
|
70
|
+
try {
|
|
71
|
+
config = JSON.parse(options.config);
|
|
72
|
+
} catch {
|
|
73
|
+
cliLogger.error("Invalid --config JSON");
|
|
74
|
+
exitProcess(1);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
cliLogger.info(`Running task: ${task.name} (${task.id})`);
|
|
80
|
+
cliLogger.info("");
|
|
81
|
+
|
|
82
|
+
const result = await runTask({
|
|
83
|
+
task,
|
|
84
|
+
config,
|
|
85
|
+
debug: options.debug,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
cliLogger.info("");
|
|
89
|
+
if (result.success) {
|
|
90
|
+
cliLogger.info(`Task completed in ${result.durationMs}ms`);
|
|
91
|
+
if (result.result !== undefined) {
|
|
92
|
+
cliLogger.info(`Result: ${JSON.stringify(result.result, null, 2)}`);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
cliLogger.error(`Task failed after ${result.durationMs}ms: ${result.error}`);
|
|
96
|
+
exitProcess(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createArgParser, parseArgsOrThrow } from "../../shared/args.js";
|
|
3
|
+
import type { ParsedArgs } from "../../shared/types.js";
|
|
4
|
+
|
|
5
|
+
const TaskArgsSchema = z.object({
|
|
6
|
+
name: z.string(),
|
|
7
|
+
config: z.string().optional(),
|
|
8
|
+
debug: z.boolean().default(false),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export type TaskArgs = z.infer<typeof TaskArgsSchema>;
|
|
12
|
+
|
|
13
|
+
export const parseTaskArgs = createArgParser(TaskArgsSchema, {
|
|
14
|
+
name: { keys: ["name"], type: "string", positional: 0 },
|
|
15
|
+
config: { keys: ["config"], type: "string" },
|
|
16
|
+
debug: { keys: ["debug"], type: "boolean" },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export async function handleTaskCommand(args: ParsedArgs): Promise<void> {
|
|
20
|
+
const opts = parseArgsOrThrow(parseTaskArgs, "task", args);
|
|
21
|
+
const { taskCommand } = await import("./command.js");
|
|
22
|
+
await taskCommand(opts);
|
|
23
|
+
}
|
|
@@ -32,6 +32,7 @@ import { demoHelp } from "../commands/demo/command-help.js";
|
|
|
32
32
|
import { mcpHelp } from "../commands/mcp/command-help.js";
|
|
33
33
|
import { issuesHelp } from "../commands/issues/command-help.js";
|
|
34
34
|
import { startHelp } from "../commands/start/command-help.js";
|
|
35
|
+
import { taskHelp } from "../commands/task/command-help.js";
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Central registry of all command help definitions.
|
|
@@ -63,4 +64,5 @@ export const COMMANDS: CommandRegistry = {
|
|
|
63
64
|
mcp: mcpHelp,
|
|
64
65
|
issues: issuesHelp,
|
|
65
66
|
start: startHelp,
|
|
67
|
+
task: taskHelp,
|
|
66
68
|
};
|
package/src/cli/router.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { handleServeCommand } from "./commands/serve/handler.js";
|
|
|
27
27
|
import { handleStartCommand } from "./commands/start/handler.js";
|
|
28
28
|
import { handleStudioCommand } from "./commands/studio/handler.js";
|
|
29
29
|
import { handleUpCommand } from "./commands/up/index.js";
|
|
30
|
+
import { handleTaskCommand } from "./commands/task/handler.js";
|
|
30
31
|
import { handleWorkerCommand } from "./commands/worker/handler.js";
|
|
31
32
|
import { login, logout, whoami } from "./auth/index.js";
|
|
32
33
|
import { parseLoginMethod } from "./auth/utils.js";
|
|
@@ -73,6 +74,7 @@ const commands: Record<string, (args: ParsedArgs) => Promise<void>> = {
|
|
|
73
74
|
"mcp": handleMCPCommand,
|
|
74
75
|
"issues": handleIssuesCommand,
|
|
75
76
|
"start": handleStartCommand,
|
|
77
|
+
"task": handleTaskCommand,
|
|
76
78
|
"worker": handleWorkerCommand,
|
|
77
79
|
};
|
|
78
80
|
|
package/src/deno.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
agentHandler,
|
|
21
21
|
promptHandler,
|
|
22
22
|
resourceHandler,
|
|
23
|
+
taskHandler,
|
|
23
24
|
toolHandler,
|
|
24
25
|
workflowHandler,
|
|
25
26
|
} from "./handlers/index.js";
|
|
@@ -89,6 +90,7 @@ export async function discoverAll(config: DiscoveryConfig): Promise<DiscoveryRes
|
|
|
89
90
|
resources: new Map(),
|
|
90
91
|
prompts: new Map(),
|
|
91
92
|
workflows: new Map(),
|
|
93
|
+
tasks: new Map(),
|
|
92
94
|
errors: [],
|
|
93
95
|
};
|
|
94
96
|
|
|
@@ -117,5 +119,10 @@ export async function discoverAll(config: DiscoveryConfig): Promise<DiscoveryRes
|
|
|
117
119
|
await discoverItems(`${baseDir}/${dir}`, result, context, workflowHandler, config.verbose);
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
// Discover tasks
|
|
123
|
+
for (const dir of config.taskDirs ?? ["tasks"]) {
|
|
124
|
+
await discoverItems(`${baseDir}/${dir}`, result, context, taskHandler, config.verbose);
|
|
125
|
+
}
|
|
126
|
+
|
|
120
127
|
return result;
|
|
121
128
|
}
|
|
@@ -9,3 +9,4 @@ export { agentHandler } from "./agent-handler.js";
|
|
|
9
9
|
export { resourceHandler } from "./resource-handler.js";
|
|
10
10
|
export { promptHandler } from "./prompt-handler.js";
|
|
11
11
|
export { workflowHandler } from "./workflow-handler.js";
|
|
12
|
+
export { taskHandler } from "./task-handler.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Discovery Handler
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { TaskDefinition } from "../../task/types.js";
|
|
6
|
+
import { isTaskDefinition } from "../../task/types.js";
|
|
7
|
+
import type { DiscoveryHandler, DiscoveryResult } from "../types.js";
|
|
8
|
+
|
|
9
|
+
export const taskHandler: DiscoveryHandler<TaskDefinition> = {
|
|
10
|
+
typeName: "task",
|
|
11
|
+
validate: (item): item is TaskDefinition => isTaskDefinition(item),
|
|
12
|
+
getId: (_task, file, dir) => {
|
|
13
|
+
// Derive ID from file path relative to tasks dir
|
|
14
|
+
let relative = file;
|
|
15
|
+
const prefix = dir.endsWith("/") ? dir : `${dir}/`;
|
|
16
|
+
if (relative.startsWith(prefix)) {
|
|
17
|
+
relative = relative.slice(prefix.length);
|
|
18
|
+
}
|
|
19
|
+
return relative.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
20
|
+
},
|
|
21
|
+
register: (_id, task) => task,
|
|
22
|
+
getResultMap: (result: DiscoveryResult) => result.tasks,
|
|
23
|
+
};
|
|
@@ -9,6 +9,7 @@ import type { Agent } from "../agent/index.js";
|
|
|
9
9
|
import type { Resource } from "../resource/index.js";
|
|
10
10
|
import type { Prompt } from "../prompt/index.js";
|
|
11
11
|
import type { Workflow } from "../workflow/index.js";
|
|
12
|
+
import type { TaskDefinition } from "../task/types.js";
|
|
12
13
|
import type { Platform } from "../platform/core-platform.js";
|
|
13
14
|
import type { FileSystemAdapter } from "../platform/adapters/base.js";
|
|
14
15
|
|
|
@@ -35,6 +36,7 @@ export interface DiscoveryConfig {
|
|
|
35
36
|
resourceDirs?: string[];
|
|
36
37
|
promptDirs?: string[];
|
|
37
38
|
workflowDirs?: string[];
|
|
39
|
+
taskDirs?: string[];
|
|
38
40
|
verbose?: boolean;
|
|
39
41
|
fsAdapter?: FileSystemAdapter;
|
|
40
42
|
}
|
|
@@ -48,6 +50,7 @@ export interface DiscoveryResult {
|
|
|
48
50
|
resources: Map<string, Resource>;
|
|
49
51
|
prompts: Map<string, Prompt>;
|
|
50
52
|
workflows: Map<string, Workflow>;
|
|
53
|
+
tasks: Map<string, TaskDefinition>;
|
|
51
54
|
errors: Array<{ file: string; error: Error }>;
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -42,8 +42,6 @@ export class VeryfrontFSAdapter implements FSAdapter {
|
|
|
42
42
|
|
|
43
43
|
/** Resolves when file list initialization is complete (for coordinating reads) */
|
|
44
44
|
private fileListReadyResolve: (() => void) | null = null;
|
|
45
|
-
/** Rejects when file list initialization fails */
|
|
46
|
-
private fileListReadyReject: ((error: Error) => void) | null = null;
|
|
47
45
|
|
|
48
46
|
private projectData?: Project;
|
|
49
47
|
private apiBaseUrl: string;
|
|
@@ -233,9 +231,8 @@ export class VeryfrontFSAdapter implements FSAdapter {
|
|
|
233
231
|
return;
|
|
234
232
|
}
|
|
235
233
|
|
|
236
|
-
const fileListReadyPromise = new Promise<void>((resolve
|
|
234
|
+
const fileListReadyPromise = new Promise<void>((resolve) => {
|
|
237
235
|
this.fileListReadyResolve = resolve;
|
|
238
|
-
this.fileListReadyReject = reject;
|
|
239
236
|
});
|
|
240
237
|
this.readOps.setFileListReadyPromise(fileListReadyPromise);
|
|
241
238
|
|
|
@@ -303,7 +300,6 @@ export class VeryfrontFSAdapter implements FSAdapter {
|
|
|
303
300
|
|
|
304
301
|
this.fileListReadyResolve?.();
|
|
305
302
|
this.fileListReadyResolve = null;
|
|
306
|
-
this.fileListReadyReject = null;
|
|
307
303
|
|
|
308
304
|
logger.debug("Fetched files during initialization", {
|
|
309
305
|
cacheKey,
|
|
@@ -357,9 +353,9 @@ export class VeryfrontFSAdapter implements FSAdapter {
|
|
|
357
353
|
this.wsManager.connect(projectId);
|
|
358
354
|
}
|
|
359
355
|
} catch (error) {
|
|
360
|
-
|
|
356
|
+
// Resolve (not reject) to avoid an unhandled-rejection crash in Deno when no lookup() is awaiting.
|
|
357
|
+
this.fileListReadyResolve?.();
|
|
361
358
|
this.fileListReadyResolve = null;
|
|
362
|
-
this.fileListReadyReject = null;
|
|
363
359
|
throw error;
|
|
364
360
|
}
|
|
365
361
|
}
|