veryfront 0.1.75 → 0.1.77

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.
Files changed (89) hide show
  1. package/README.md +2 -0
  2. package/esm/cli/commands/files/command.d.ts +3 -3
  3. package/esm/cli/commands/pull/command.js +2 -2
  4. package/esm/cli/commands/task/command.d.ts.map +1 -1
  5. package/esm/cli/commands/task/command.js +57 -51
  6. package/esm/cli/commands/workflow/command-help.d.ts +3 -0
  7. package/esm/cli/commands/workflow/command-help.d.ts.map +1 -0
  8. package/esm/cli/commands/workflow/command-help.js +19 -0
  9. package/esm/cli/commands/workflow/command.d.ts +5 -0
  10. package/esm/cli/commands/workflow/command.d.ts.map +1 -0
  11. package/esm/cli/commands/workflow/command.js +119 -0
  12. package/esm/cli/commands/workflow/handler.d.ts +28 -0
  13. package/esm/cli/commands/workflow/handler.d.ts.map +1 -0
  14. package/esm/cli/commands/workflow/handler.js +19 -0
  15. package/esm/cli/help/command-definitions.d.ts.map +1 -1
  16. package/esm/cli/help/command-definitions.js +2 -0
  17. package/esm/cli/router.d.ts.map +1 -1
  18. package/esm/cli/router.js +2 -0
  19. package/esm/cli/shared/project-source-context.d.ts +18 -0
  20. package/esm/cli/shared/project-source-context.d.ts.map +1 -0
  21. package/esm/cli/shared/project-source-context.js +52 -0
  22. package/esm/deno.d.ts +6 -0
  23. package/esm/deno.js +12 -6
  24. package/esm/src/data/server-data-fetcher.d.ts.map +1 -1
  25. package/esm/src/data/server-data-fetcher.js +17 -1
  26. package/esm/src/jobs/index.d.ts +34 -0
  27. package/esm/src/jobs/index.d.ts.map +1 -0
  28. package/esm/src/jobs/index.js +33 -0
  29. package/esm/src/jobs/jobs-client.d.ts +134 -0
  30. package/esm/src/jobs/jobs-client.d.ts.map +1 -0
  31. package/esm/src/jobs/jobs-client.js +218 -0
  32. package/esm/src/jobs/schemas.d.ts +1304 -0
  33. package/esm/src/jobs/schemas.d.ts.map +1 -0
  34. package/esm/src/jobs/schemas.js +159 -0
  35. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts +3 -3
  36. package/esm/src/platform/adapters/veryfront-api-client/operations.d.ts.map +1 -1
  37. package/esm/src/platform/adapters/veryfront-api-client/operations.js +12 -10
  38. package/esm/src/platform/adapters/veryfront-api-client/retry-handler.d.ts +4 -0
  39. package/esm/src/platform/adapters/veryfront-api-client/retry-handler.d.ts.map +1 -1
  40. package/esm/src/platform/adapters/veryfront-api-client/retry-handler.js +12 -6
  41. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.d.ts +4 -4
  42. package/esm/src/platform/adapters/veryfront-api-client/schemas/api.schema.js +4 -4
  43. package/esm/src/platform/index.d.ts +1 -0
  44. package/esm/src/platform/index.d.ts.map +1 -1
  45. package/esm/src/platform/index.js +1 -0
  46. package/esm/src/proxy/handler.d.ts.map +1 -1
  47. package/esm/src/proxy/handler.js +21 -21
  48. package/esm/src/routing/api/route-executor.d.ts.map +1 -1
  49. package/esm/src/routing/api/route-executor.js +30 -3
  50. package/esm/src/security/deno-permissions.d.ts +1 -1
  51. package/esm/src/security/deno-permissions.d.ts.map +1 -1
  52. package/esm/src/security/deno-permissions.js +2 -1
  53. package/esm/src/security/sandbox/project-worker.d.ts.map +1 -1
  54. package/esm/src/security/sandbox/project-worker.js +2 -2
  55. package/esm/src/security/sandbox/worker-permissions.d.ts.map +1 -1
  56. package/esm/src/security/sandbox/worker-permissions.js +23 -20
  57. package/esm/src/security/sandbox/worker-pool.d.ts.map +1 -1
  58. package/esm/src/security/sandbox/worker-pool.js +17 -14
  59. package/esm/src/security/sandbox/worker-types.d.ts +2 -0
  60. package/esm/src/security/sandbox/worker-types.d.ts.map +1 -1
  61. package/esm/src/security/sandbox/worker-types.js +2 -0
  62. package/esm/src/task/types.d.ts +6 -0
  63. package/esm/src/task/types.d.ts.map +1 -1
  64. package/package.json +5 -1
  65. package/src/cli/commands/pull/command.ts +3 -3
  66. package/src/cli/commands/task/command.ts +66 -57
  67. package/src/cli/commands/workflow/command-help.ts +21 -0
  68. package/src/cli/commands/workflow/command.ts +152 -0
  69. package/src/cli/commands/workflow/handler.ts +25 -0
  70. package/src/cli/help/command-definitions.ts +2 -0
  71. package/src/cli/router.ts +2 -0
  72. package/src/cli/shared/project-source-context.ts +96 -0
  73. package/src/deno.js +12 -6
  74. package/src/src/data/server-data-fetcher.ts +30 -2
  75. package/src/src/jobs/index.ts +85 -0
  76. package/src/src/jobs/jobs-client.ts +503 -0
  77. package/src/src/jobs/schemas.ts +202 -0
  78. package/src/src/platform/adapters/veryfront-api-client/operations.ts +14 -14
  79. package/src/src/platform/adapters/veryfront-api-client/retry-handler.ts +15 -6
  80. package/src/src/platform/adapters/veryfront-api-client/schemas/api.schema.ts +4 -4
  81. package/src/src/platform/index.ts +1 -0
  82. package/src/src/proxy/handler.ts +27 -19
  83. package/src/src/routing/api/route-executor.ts +43 -7
  84. package/src/src/security/deno-permissions.ts +2 -1
  85. package/src/src/security/sandbox/project-worker.ts +2 -2
  86. package/src/src/security/sandbox/worker-permissions.ts +22 -19
  87. package/src/src/security/sandbox/worker-pool.ts +21 -13
  88. package/src/src/security/sandbox/worker-types.ts +3 -0
  89. package/src/src/task/types.ts +6 -0
@@ -11,6 +11,19 @@
11
11
  * See: https://docs.deno.com/runtime/fundamentals/permissions/
12
12
  */
13
13
  import * as dntShim from "../../../_dnt.shims.js";
14
+ // Cache compiled binary check — Deno.execPath() is a syscall that never changes at runtime
15
+ const _isCompiledBinary = (() => {
16
+ try {
17
+ const exec = typeof dntShim.Deno !== "undefined" ? dntShim.Deno.execPath?.() : undefined;
18
+ if (!exec)
19
+ return false;
20
+ const name = exec.split(/[/\\]/).pop()?.toLowerCase() ?? "";
21
+ return name !== "deno" && name !== "deno.exe";
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ })();
14
27
  /**
15
28
  * Build scoped permissions for a project worker.
16
29
  *
@@ -27,26 +40,16 @@ export function buildWorkerPermissions(readPaths) {
27
40
  // is outside the project directory. Rather than trying to enumerate all
28
41
  // read paths, grant full read access — the security boundary is enforced
29
42
  // by denying write/run/ffi/sys, not by restricting reads.
30
- // Check for compiled binary by testing if execPath is NOT "deno"/"deno.exe"
31
- try {
32
- const exec = typeof dntShim.Deno !== "undefined" ? dntShim.Deno.execPath?.() : undefined;
33
- if (exec) {
34
- const name = exec.split(/[/\\]/).pop()?.toLowerCase() ?? "";
35
- if (name !== "deno" && name !== "deno.exe") {
36
- return {
37
- read: true,
38
- write: false,
39
- net: true,
40
- env: true,
41
- run: false,
42
- ffi: false,
43
- sys: false,
44
- };
45
- }
46
- }
47
- }
48
- catch {
49
- // execPath may not be available
43
+ if (_isCompiledBinary) {
44
+ return {
45
+ read: true,
46
+ write: false,
47
+ net: true,
48
+ env: true,
49
+ run: false,
50
+ ffi: false,
51
+ sys: false,
52
+ };
50
53
  }
51
54
  return {
52
55
  read: readPaths.length > 0 ? readPaths : false,
@@ -1 +1 @@
1
- {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../../../src/src/security/sandbox/worker-pool.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAWzF,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,MAAM,CAAmB;IAEjC,OAAO,CAAC,eAAe,CAAqD;IAC5E,OAAO,CAAC,mBAAmB,CAAqD;gBAEpE,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM;IAMlD;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,aAAa;IA0CxE;;;OAGG;IACH,OAAO,CACL,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,cAAc,CAAC;IAkD1B;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAUpC;;OAEG;IACH,QAAQ,IAAI;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC;YACf,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,OAAO,CAAC;YACpB,MAAM,EAAE,MAAM,CAAC;YACf,KAAK,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ;IA4BD;;OAEG;IACH,UAAU,IAAI;QACZ,uCAAuC;QACvC,cAAc,EAAE,MAAM,CAAC;QACvB,oDAAoD;QACpD,kBAAkB,EAAE,MAAM,CAAC;QAC3B,kDAAkD;QAClD,sBAAsB,EAAE,MAAM,CAAC;QAC/B,qDAAqD;QACrD,WAAW,EAAE,MAAM,CAAC;QACpB,kEAAkE;QAClE,cAAc,EAAE,MAAM,CAAC;KACxB;IAoBD;;OAEG;IACH,QAAQ,IAAI,IAAI;IAgBhB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,aAAa;YA4BP,WAAW;IAmBzB;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;CA8BjC;AAqBD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAGlD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAG/C;AAKD,wBAAgB,aAAa,IAAI,UAAU,CAiB1C;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C"}
1
+ {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../../../src/src/security/sandbox/worker-pool.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAWzF,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,MAAM,CAAmB;IAEjC,OAAO,CAAC,eAAe,CAAqD;IAC5E,OAAO,CAAC,mBAAmB,CAAqD;gBAEpE,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM;IAMlD;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,aAAa;IA0CxE;;;OAGG;IACH,OAAO,CACL,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,cAAc,CAAC;IA0D1B;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAUpC;;OAEG;IACH,QAAQ,IAAI;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC;YACf,YAAY,EAAE,MAAM,CAAC;YACrB,UAAU,EAAE,OAAO,CAAC;YACpB,MAAM,EAAE,MAAM,CAAC;YACf,KAAK,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ;IA4BD;;OAEG;IACH,UAAU,IAAI;QACZ,uCAAuC;QACvC,cAAc,EAAE,MAAM,CAAC;QACvB,oDAAoD;QACpD,kBAAkB,EAAE,MAAM,CAAC;QAC3B,kDAAkD;QAClD,sBAAsB,EAAE,MAAM,CAAC;QAC/B,qDAAqD;QACrD,WAAW,EAAE,MAAM,CAAC;QACpB,kEAAkE;QAClE,cAAc,EAAE,MAAM,CAAC;KACxB;IAoBD;;OAEG;IACH,QAAQ,IAAI,IAAI;IAgBhB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,aAAa;YA4BP,WAAW;IAmBzB;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;CA8BjC;AAqBD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAGlD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAG/C;AAKD,wBAAgB,aAAa,IAAI,UAAU,CAiB1C;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C"}
@@ -85,22 +85,25 @@ export class WorkerPool {
85
85
  (entry && Date.now() - entry.createdAt > this.config.maxWorkerAgeMs);
86
86
  if (shouldRecycle && !this.recycling.has(projectId)) {
87
87
  this.recycling.add(projectId);
88
- try {
89
- logger.debug("Recycling worker", {
90
- projectId,
91
- requestCount: worker.requestCount,
92
- ageMs: entry ? Date.now() - entry.createdAt : 0,
93
- reason: worker.requestCount >= this.config.maxRequestsPerWorker
94
- ? "request_count"
95
- : "age",
96
- });
88
+ logger.debug("Recycling worker", {
89
+ projectId,
90
+ requestCount: worker.requestCount,
91
+ ageMs: entry ? Date.now() - entry.createdAt : 0,
92
+ reason: worker.requestCount >= this.config.maxRequestsPerWorker
93
+ ? "request_count"
94
+ : "age",
95
+ });
96
+ // Warm replacement: let the old worker handle this last request,
97
+ // then evict it and create a replacement after the request settles.
98
+ // This avoids cold-start latency for the caller AND prevents the
99
+ // old worker from being terminated while it still has pending work.
100
+ const result = worker.execute(request);
101
+ void result.finally(() => {
97
102
  this.evictWorker(projectId);
98
- const fresh = this.getOrCreateWorker(projectId, readPaths);
99
- return fresh.execute(request);
100
- }
101
- finally {
103
+ this.getOrCreateWorker(projectId, readPaths);
102
104
  this.recycling.delete(projectId);
103
- }
105
+ });
106
+ return result;
104
107
  }
105
108
  return worker.execute(request);
106
109
  }, { "workerPool.projectId": projectId });
@@ -161,5 +161,7 @@ export interface WorkerPoolConfig {
161
161
  /** Per-worker memory budget in MB (default: 64). Workers exceeding this are evicted. */
162
162
  memoryBudgetMb: number;
163
163
  }
164
+ /** Maximum request body size for worker isolation (10 MB) */
165
+ export declare const MAX_WORKER_BODY_BYTES: number;
164
166
  export declare const DEFAULT_WORKER_POOL_CONFIG: WorkerPoolConfig;
165
167
  //# sourceMappingURL=worker-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-types.d.ts","sourceRoot":"","sources":["../../../../src/src/security/sandbox/worker-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worker-types.d.ts","sourceRoot":"","sources":["../../../../src/src/security/sandbox/worker-types.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,8 @@
6
6
  *
7
7
  * @module security/sandbox/worker-types
8
8
  */
9
+ /** Maximum request body size for worker isolation (10 MB) */
10
+ export const MAX_WORKER_BODY_BYTES = 10 * 1024 * 1024;
9
11
  export const DEFAULT_WORKER_POOL_CONFIG = {
10
12
  maxPoolSize: 20,
11
13
  idleTimeoutMs: 300_000,
@@ -24,6 +24,12 @@ export interface TaskDefinition {
24
24
  name?: string;
25
25
  /** Task description */
26
26
  description?: string;
27
+ /** Optional JSON-schema-like input contract surfaced in APIs/UIs */
28
+ inputSchema?: Record<string, unknown>;
29
+ /** Optional JSON-schema-like output contract surfaced in APIs/UIs */
30
+ outputSchema?: Record<string, unknown>;
31
+ /** Whether this task can be scheduled via cron jobs */
32
+ schedulable?: boolean;
27
33
  /** The function to execute */
28
34
  run: (ctx: TaskContext) => Promise<unknown> | unknown;
29
35
  }
@@ -1 +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"}
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,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,uDAAuD;IACvD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.75",
3
+ "version": "0.1.77",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -76,6 +76,9 @@
76
76
  "./resource": {
77
77
  "import": "./esm/src/resource/index.js"
78
78
  },
79
+ "./jobs": {
80
+ "import": "./esm/src/jobs/index.js"
81
+ },
79
82
  "./mcp": {
80
83
  "import": "./esm/src/mcp/index.js"
81
84
  },
@@ -149,6 +152,7 @@
149
152
  "esbuild": "0.20.2",
150
153
  "github-slugger": "2.0.0",
151
154
  "gray-matter": "4.0.3",
155
+ "jose": "5.9.6",
152
156
  "mdast-util-to-string": "4.0.0",
153
157
  "mime-types": "2.1.35",
154
158
  "react": "19.1.1",
@@ -159,7 +159,7 @@ export function buildFilesListUrl(projectSlug: string, source: PullSource): stri
159
159
  case "release":
160
160
  return `/projects/${projectSlug}/releases/${encodeURIComponent(source.version)}/files`;
161
161
  case "branch":
162
- return `/projects/${projectSlug}/branches/${encodeURIComponent(source.name)}/files`;
162
+ return `/projects/${projectSlug}/files?branch=${encodeURIComponent(source.name)}`;
163
163
  case "main":
164
164
  return `/projects/${projectSlug}/files`;
165
165
  }
@@ -211,9 +211,9 @@ export function buildFileContentUrl(projectSlug: string, path: string, source: P
211
211
  encodeURIComponent(source.version)
212
212
  }/files/${encodedPath}`;
213
213
  case "branch":
214
- return `/projects/${projectSlug}/branches/${
214
+ return `/projects/${projectSlug}/files/${encodedPath}?branch=${
215
215
  encodeURIComponent(source.name)
216
- }/files/${encodedPath}`;
216
+ }`;
217
217
  case "main":
218
218
  return `/projects/${projectSlug}/files/${encodedPath}`;
219
219
  }
@@ -9,14 +9,12 @@ import * as dntShim from "../../../_dnt.shims.js";
9
9
 
10
10
  import { cliLogger } from "../../utils/index.js";
11
11
  import { exitProcess } from "../../utils/index.js";
12
+ import { withProjectSourceContext } from "../../shared/project-source-context.js";
12
13
  import type { TaskArgs } from "./handler.js";
13
14
 
14
15
  export interface TaskOptions extends TaskArgs {}
15
16
 
16
17
  export async function taskCommand(options: TaskOptions): Promise<void> {
17
- const { getAdapter } = await import(
18
- "../../../src/platform/adapters/detect.js"
19
- );
20
18
  const { discoverTasks } = await import(
21
19
  "../../../src/task/discovery.js"
22
20
  );
@@ -32,67 +30,78 @@ export async function taskCommand(options: TaskOptions): Promise<void> {
32
30
  }
33
31
 
34
32
  const projectDir = dntShim.Deno.cwd();
35
- const adapter = await getAdapter();
33
+ await withProjectSourceContext(
34
+ projectDir,
35
+ async ({ adapter, config, projectId, proxyContext }) => {
36
+ const sourceLabel = proxyContext?.branchRef
37
+ ? `branch ${proxyContext.branchRef}`
38
+ : proxyContext
39
+ ? "main"
40
+ : `${projectDir}/tasks/...`;
36
41
 
37
- cliLogger.info(`Discovering tasks in ${projectDir}/tasks/...`);
42
+ cliLogger.info(`Discovering tasks in ${sourceLabel}`);
38
43
 
39
- const { tasks, errors } = await discoverTasks({
40
- projectDir,
41
- adapter,
42
- debug: options.debug,
43
- });
44
+ const { tasks, errors } = await discoverTasks({
45
+ projectDir,
46
+ adapter,
47
+ config,
48
+ debug: options.debug,
49
+ });
44
50
 
45
- if (errors.length > 0 && options.debug) {
46
- for (const err of errors) {
47
- cliLogger.warn(` Warning: ${err.filePath}: ${err.error}`);
48
- }
49
- }
51
+ if (errors.length > 0 && options.debug) {
52
+ for (const err of errors) {
53
+ cliLogger.warn(` Warning: ${err.filePath}: ${err.error}`);
54
+ }
55
+ }
50
56
 
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})` : ""}`);
57
+ const task = tasks.find((t) => t.id === taskName);
58
+ if (!task) {
59
+ cliLogger.error(`Task "${taskName}" not found.`);
60
+ if (tasks.length > 0) {
61
+ cliLogger.info("Available tasks:");
62
+ for (const t of tasks) {
63
+ cliLogger.info(` - ${t.id}${t.name !== t.id ? ` (${t.name})` : ""}`);
64
+ }
65
+ } else {
66
+ cliLogger.info("No tasks found. Create a task file in tasks/ directory:");
67
+ cliLogger.info(" tasks/my-task.ts");
68
+ }
69
+ exitProcess(1);
70
+ return;
58
71
  }
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
72
 
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
- }
73
+ let taskConfig: Record<string, unknown> = {};
74
+ if (options.config) {
75
+ try {
76
+ taskConfig = JSON.parse(options.config);
77
+ } catch {
78
+ cliLogger.error("Invalid --config JSON");
79
+ exitProcess(1);
80
+ return;
81
+ }
82
+ }
78
83
 
79
- cliLogger.info(`Running task: ${task.name} (${task.id})`);
80
- cliLogger.info("");
84
+ cliLogger.info(`Running task: ${task.name} (${task.id})`);
85
+ cliLogger.info("");
81
86
 
82
- const result = await runTask({
83
- task,
84
- config,
85
- debug: options.debug,
86
- });
87
+ const result = await runTask({
88
+ task,
89
+ config: taskConfig,
90
+ projectId,
91
+ debug: options.debug,
92
+ });
87
93
 
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
- }
94
+ cliLogger.info("");
95
+ if (result.success) {
96
+ cliLogger.info(`Task completed in ${result.durationMs}ms`);
97
+ if (result.result !== undefined) {
98
+ cliLogger.info(`Result: ${JSON.stringify(result.result, null, 2)}`);
99
+ }
100
+ return;
101
+ }
102
+
103
+ cliLogger.error(`Task failed after ${result.durationMs}ms: ${result.error}`);
104
+ exitProcess(1);
105
+ },
106
+ );
98
107
  }
@@ -0,0 +1,21 @@
1
+ import type { CommandHelp } from "../../help/types.js";
2
+
3
+ export const workflowHelp: CommandHelp = {
4
+ name: "workflow",
5
+ description: "Run a workflow from the app/workflows directory",
6
+ usage: "veryfront workflow run <id> [options]",
7
+ options: [
8
+ {
9
+ flag: "--input <json>",
10
+ description: "JSON input to pass to the workflow",
11
+ },
12
+ {
13
+ flag: "--debug",
14
+ description: "Enable debug logging",
15
+ },
16
+ ],
17
+ examples: [
18
+ "veryfront workflow run publish-site",
19
+ 'veryfront workflow run content-pipeline --input \'{"topic":"AI"}\'',
20
+ ],
21
+ };
@@ -0,0 +1,152 @@
1
+ import * as dntShim from "../../../_dnt.shims.js";
2
+ import { cliLogger } from "../../utils/index.js";
3
+ import { exitProcess } from "../../utils/index.js";
4
+ import { withProjectSourceContext } from "../../shared/project-source-context.js";
5
+ import { getEnv } from "../../../src/platform/index.js";
6
+ import type { WorkflowArgs } from "./handler.js";
7
+
8
+ const WORKFLOW_STATUS_POLL_INTERVAL_MS = 1_000;
9
+
10
+ export interface WorkflowOptions extends WorkflowArgs {}
11
+
12
+ async function createWorkflowClient(debug: boolean) {
13
+ const { createWorkflowClient } = await import(
14
+ "../../../src/workflow/api/workflow-client.js"
15
+ );
16
+
17
+ const redisUrl = getEnv("REDIS_URL")?.trim();
18
+ if (!redisUrl) {
19
+ return createWorkflowClient({ debug });
20
+ }
21
+
22
+ const { RedisBackend } = await import(
23
+ "../../../src/workflow/backends/redis.js"
24
+ );
25
+
26
+ const backend = new RedisBackend({ url: redisUrl, debug });
27
+ if (backend.initialize) {
28
+ await backend.initialize();
29
+ }
30
+
31
+ return createWorkflowClient({ backend, debug });
32
+ }
33
+
34
+ async function waitForWorkflowExit(
35
+ client: Awaited<ReturnType<typeof createWorkflowClient>>,
36
+ runId: string,
37
+ ): Promise<void> {
38
+ while (true) {
39
+ const run = await client.getRun(runId);
40
+ if (!run) {
41
+ throw new Error(`Workflow run not found: ${runId}`);
42
+ }
43
+
44
+ if (run.status === "completed") {
45
+ cliLogger.info(`Workflow completed: ${runId}`);
46
+ if (run.output !== undefined) {
47
+ cliLogger.info(`Result: ${JSON.stringify(run.output, null, 2)}`);
48
+ }
49
+ return;
50
+ }
51
+
52
+ if (run.status === "waiting") {
53
+ cliLogger.info(`Workflow is waiting: ${runId}`);
54
+ return;
55
+ }
56
+
57
+ if (run.status === "failed") {
58
+ throw new Error(run.error?.message || `Workflow failed: ${runId}`);
59
+ }
60
+
61
+ if (run.status === "cancelled") {
62
+ throw new Error(`Workflow was cancelled: ${runId}`);
63
+ }
64
+
65
+ await new Promise((resolve) => dntShim.setTimeout(resolve, WORKFLOW_STATUS_POLL_INTERVAL_MS));
66
+ }
67
+ }
68
+
69
+ export async function workflowCommand(options: WorkflowOptions): Promise<void> {
70
+ if (options.action !== "run") {
71
+ cliLogger.error(`Unknown workflow action: ${options.action}`);
72
+ exitProcess(1);
73
+ return;
74
+ }
75
+
76
+ const workflowId = options.name;
77
+ if (!workflowId) {
78
+ cliLogger.error("Workflow ID is required. Usage: veryfront workflow run <id>");
79
+ exitProcess(1);
80
+ return;
81
+ }
82
+
83
+ let input: Record<string, unknown> = {};
84
+ if (options.input) {
85
+ try {
86
+ input = JSON.parse(options.input);
87
+ } catch {
88
+ cliLogger.error("Invalid --input JSON");
89
+ exitProcess(1);
90
+ return;
91
+ }
92
+ }
93
+
94
+ const { discoverWorkflows } = await import(
95
+ "../../../src/workflow/discovery/index.js"
96
+ );
97
+
98
+ await withProjectSourceContext(dntShim.Deno.cwd(), async ({ adapter, config, proxyContext }) => {
99
+ const sourceLabel = proxyContext?.branchRef
100
+ ? `branch ${proxyContext.branchRef}`
101
+ : proxyContext
102
+ ? "main"
103
+ : `${dntShim.Deno.cwd()}/app/workflows/...`;
104
+
105
+ cliLogger.info(`Discovering workflows in ${sourceLabel}`);
106
+
107
+ const discovery = await discoverWorkflows({
108
+ projectDir: dntShim.Deno.cwd(),
109
+ adapter,
110
+ config,
111
+ debug: options.debug,
112
+ });
113
+
114
+ if (discovery.errors.length > 0 && options.debug) {
115
+ for (const err of discovery.errors) {
116
+ cliLogger.warn(` Warning: ${err.filePath}: ${err.error}`);
117
+ }
118
+ }
119
+
120
+ const workflow = discovery.workflows.find((candidate) => candidate.id === workflowId);
121
+ if (!workflow) {
122
+ cliLogger.error(`Workflow "${workflowId}" not found.`);
123
+ if (discovery.workflows.length > 0) {
124
+ cliLogger.info("Available workflows:");
125
+ for (const candidate of discovery.workflows) {
126
+ cliLogger.info(` - ${candidate.id}`);
127
+ }
128
+ } else {
129
+ cliLogger.info("No workflows found. Create a workflow file in app/workflows/.");
130
+ }
131
+ exitProcess(1);
132
+ return;
133
+ }
134
+
135
+ const client = await createWorkflowClient(options.debug);
136
+
137
+ try {
138
+ client.register(workflow.definition);
139
+ cliLogger.info(`Running workflow: ${workflow.id}`);
140
+ cliLogger.info("");
141
+
142
+ const handle = await client.start(workflow.id, input);
143
+ await waitForWorkflowExit(client, handle.runId);
144
+ } finally {
145
+ await client.destroy();
146
+ }
147
+ }).catch((error: unknown) => {
148
+ const message = error instanceof Error ? error.message : String(error);
149
+ cliLogger.error(message);
150
+ exitProcess(1);
151
+ });
152
+ }
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ import { createArgParser, parseArgsOrThrow } from "../../shared/args.js";
3
+ import type { ParsedArgs } from "../../shared/types.js";
4
+
5
+ const WorkflowArgsSchema = z.object({
6
+ action: z.enum(["run"]),
7
+ name: z.string(),
8
+ input: z.string().optional(),
9
+ debug: z.boolean().default(false),
10
+ });
11
+
12
+ export type WorkflowArgs = z.infer<typeof WorkflowArgsSchema>;
13
+
14
+ export const parseWorkflowArgs = createArgParser(WorkflowArgsSchema, {
15
+ action: { keys: ["action"], type: "string", positional: 0 },
16
+ name: { keys: ["name"], type: "string", positional: 1 },
17
+ input: { keys: ["input"], type: "string" },
18
+ debug: { keys: ["debug"], type: "boolean" },
19
+ });
20
+
21
+ export async function handleWorkflowCommand(args: ParsedArgs): Promise<void> {
22
+ const opts = parseArgsOrThrow(parseWorkflowArgs, "workflow", args);
23
+ const { workflowCommand } = await import("./command.js");
24
+ await workflowCommand(opts);
25
+ }
@@ -36,6 +36,7 @@ import { mcpHelp } from "../commands/mcp/command-help.js";
36
36
  import { issuesHelp } from "../commands/issues/command-help.js";
37
37
  import { startHelp } from "../commands/start/command-help.js";
38
38
  import { taskHelp } from "../commands/task/command-help.js";
39
+ import { workflowHelp } from "../commands/workflow/command-help.js";
39
40
 
40
41
  /**
41
42
  * Central registry of all command help definitions.
@@ -71,4 +72,5 @@ export const COMMANDS: CommandRegistry = {
71
72
  issues: issuesHelp,
72
73
  start: startHelp,
73
74
  task: taskHelp,
75
+ workflow: workflowHelp,
74
76
  };
package/src/cli/router.ts CHANGED
@@ -31,6 +31,7 @@ import { handleStartCommand } from "./commands/start/handler.js";
31
31
  import { handleStudioCommand } from "./commands/studio/handler.js";
32
32
  import { handleUpCommand } from "./commands/up/index.js";
33
33
  import { handleTaskCommand } from "./commands/task/handler.js";
34
+ import { handleWorkflowCommand } from "./commands/workflow/handler.js";
34
35
  import { handleWorkerCommand } from "./commands/worker/handler.js";
35
36
  import { login, logout, whoami } from "./auth/index.js";
36
37
  import { parseLoginMethod } from "./auth/utils.js";
@@ -81,6 +82,7 @@ const commands: Record<string, (args: ParsedArgs) => Promise<void>> = {
81
82
  "issues": handleIssuesCommand,
82
83
  "start": handleStartCommand,
83
84
  "task": handleTaskCommand,
85
+ "workflow": handleWorkflowCommand,
84
86
  "worker": handleWorkerCommand,
85
87
  };
86
88