@celsian/vura-cli 0.5.7 → 0.5.10

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/README.md CHANGED
@@ -6,7 +6,7 @@ CLI for [Vura](https://vura.io) — develop, build, and run tasks for Vura appli
6
6
 
7
7
  ## What it does
8
8
 
9
- `@celsian/vura-cli` provides the `vura` command for developing and building Vura projects. It scans routes, starts the Vite dev server with API middleware, bundles for production, lets you inspect runtime placement, and lets you run task routes by name from the terminal. `vura deploy` is reserved for the managed Vura Platform and intentionally fails closed in the OSS CLI use an adapter (`adapter-lambda`, `adapter-cloudflare`) to self-host. The package was historically named `then`/`thenjs`; the only installed bin is `vura`.
9
+ `@celsian/vura-cli` provides the `vura` command for developing, building, inspecting, and deploying Vura projects. It reports effective Function/Dedicated placement and pending Edge requests, including memory, CPU, timeout, provider recommendation, confidence, and reasons. `create-vura` installs the managed deployment adapter, so `vura deploy` works without a follow-up package install. Self-hosted builds can use the Lambda or Cloudflare adapters. The package was historically named `then`/`thenjs`; the only installed bin is `vura`.
10
10
 
11
11
  ## Install
12
12
 
@@ -44,6 +44,10 @@ vura routes inspect --json
44
44
  vura runtime advise --json
45
45
  ```
46
46
 
47
+ An Edge declaration is a request, not an override. The CLI reports it as
48
+ pending while the route continues on 1 GiB Function compute; only measured
49
+ platform eligibility can promote it to the fixed 128 MiB Edge runtime.
50
+
47
51
  (`then` is a shell reserved word — use `vura` in all scripts.)
48
52
 
49
53
  ## Documentation
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * `vura deploy` — deploy the built project to the Vura platform.
3
3
  *
4
- * Packages `dist/`, uploads it to the Vura API, streams build logs, and prints
5
- * the resulting deployment URL. The actual upload/poll flow lives in
4
+ * Packages `dist/` (plus runtime dependencies for Dedicated/server builds),
5
+ * uploads it to the Vura API, streams build logs, and prints the resulting
6
+ * deployment URL. The actual upload/poll flow lives in
6
7
  * `@celsian/vura-adapter-vura` (`deployToVura`) so the adapter's `buildEnd`
7
8
  * hook and this command share one implementation.
8
9
  *
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * `vura deploy` — deploy the built project to the Vura platform.
3
3
  *
4
- * Packages `dist/`, uploads it to the Vura API, streams build logs, and prints
5
- * the resulting deployment URL. The actual upload/poll flow lives in
4
+ * Packages `dist/` (plus runtime dependencies for Dedicated/server builds),
5
+ * uploads it to the Vura API, streams build logs, and prints the resulting
6
+ * deployment URL. The actual upload/poll flow lives in
6
7
  * `@celsian/vura-adapter-vura` (`deployToVura`) so the adapter's `buildEnd`
7
8
  * hook and this command share one implementation.
8
9
  *
@@ -92,6 +93,7 @@ export async function deployCommand(args) {
92
93
  try {
93
94
  const result = await deployToVura({
94
95
  distDir,
96
+ projectRoot,
95
97
  apiUrl,
96
98
  token,
97
99
  projectId,
@@ -1,4 +1,4 @@
1
- import type { ApiRoute, PageRoute, RouteManifest } from '@celsian/vura-core';
1
+ import type { ApiRoute, ComputeClass, PageRoute, RouteManifest } from '@celsian/vura-core';
2
2
  export type RuntimeProfile = 'static' | 'cold' | 'hot' | 'streaming-hot' | 'task-cold' | 'cron-cold' | 'task-hot' | 'cron-hot';
3
3
  export interface RuntimeRouteInspection {
4
4
  type: 'api' | 'page';
@@ -10,6 +10,15 @@ export interface RuntimeRouteInspection {
10
10
  methods?: string[];
11
11
  schedule?: string;
12
12
  hasWebsocket?: boolean;
13
+ effectiveComputeClass?: 'function' | 'dedicated';
14
+ requestedComputeClass?: ComputeClass;
15
+ edgeEligibility?: 'pending';
16
+ memory?: string | number;
17
+ cpu?: number;
18
+ timeout?: number;
19
+ providerRecommendation?: 'elastic-function-provider' | 'dedicated-fly-machine' | 'cloudflare-workers-for-platforms';
20
+ confidence?: 'high' | 'medium';
21
+ reasons?: string[];
13
22
  warnings: string[];
14
23
  nextCommand?: string;
15
24
  }
@@ -6,8 +6,74 @@ function configNumber(config, key) {
6
6
  const value = config[key];
7
7
  return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
8
8
  }
9
+ function configObject(config, key) {
10
+ const value = config[key];
11
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
12
+ ? value
13
+ : undefined;
14
+ }
15
+ function computeForRoute(route) {
16
+ return configObject(route.config, 'compute') ?? { class: route.kind === 'hot' ? 'dedicated' : 'function', memory: '1gb' };
17
+ }
18
+ function computeDetails(route) {
19
+ const compute = computeForRoute(route);
20
+ const effectiveComputeClass = compute.effectiveClass === 'dedicated' || compute.class === 'dedicated'
21
+ ? 'dedicated'
22
+ : 'function';
23
+ const requestedComputeClass = compute.class === 'edge' || compute.requestedClass === 'edge'
24
+ ? 'edge'
25
+ : effectiveComputeClass;
26
+ const effectiveMemory = compute.effectiveMemory ?? compute.memory;
27
+ const memory = typeof effectiveMemory === 'string' || typeof effectiveMemory === 'number'
28
+ ? effectiveMemory
29
+ : undefined;
30
+ const cpu = typeof compute.cpu === 'number' ? compute.cpu : undefined;
31
+ const timeout = configNumber(route.config, 'timeout');
32
+ const edgeEligibility = compute.edgeEligibility === 'pending' ? 'pending' : undefined;
33
+ if (requestedComputeClass === 'edge') {
34
+ return {
35
+ effectiveComputeClass,
36
+ requestedComputeClass,
37
+ edgeEligibility,
38
+ memory,
39
+ cpu,
40
+ timeout,
41
+ providerRecommendation: 'cloudflare-workers-for-platforms',
42
+ confidence: 'high',
43
+ reasons: [
44
+ 'Edge is an optimization request and remains on Function until the platform marks this endpoint eligible from observed memory/runtime telemetry.',
45
+ 'Edge has a fixed 128mb isolate ceiling; the safe fallback is Function at 1gb.',
46
+ ],
47
+ };
48
+ }
49
+ if (effectiveComputeClass === 'dedicated') {
50
+ return {
51
+ effectiveComputeClass,
52
+ requestedComputeClass,
53
+ memory,
54
+ cpu,
55
+ timeout,
56
+ providerRecommendation: 'dedicated-fly-machine',
57
+ confidence: 'high',
58
+ reasons: [route.hasWebsocket
59
+ ? 'WebSocket upgrades require persistent Dedicated compute.'
60
+ : 'The route explicitly requests persistent Dedicated compute.'],
61
+ };
62
+ }
63
+ return {
64
+ effectiveComputeClass,
65
+ requestedComputeClass,
66
+ memory,
67
+ cpu,
68
+ timeout,
69
+ providerRecommendation: 'elastic-function-provider',
70
+ confidence: 'medium',
71
+ reasons: ['Stateless endpoints and tasks default to scale-to-zero Function compute at 1gb.'],
72
+ };
73
+ }
9
74
  function prefersHotTask(config) {
10
- return ['runtime', 'placement', 'target'].some((key) => configString(config, key) === 'hot')
75
+ return configObject(config, 'compute')?.class === 'dedicated'
76
+ || ['runtime', 'placement', 'target'].some((key) => configString(config, key) === 'hot')
11
77
  || config.hot === true;
12
78
  }
13
79
  export function taskNameFromPattern(urlPattern) {
@@ -53,6 +119,10 @@ function inspectApiRoute(route) {
53
119
  if (route.hasWebsocket && route.kind !== 'hot') {
54
120
  warnings.push('WebSocket exports require kind: hot to be reachable.');
55
121
  }
122
+ const details = computeDetails(route);
123
+ if (details.requestedComputeClass === 'edge' && details.edgeEligibility === 'pending') {
124
+ warnings.push('Edge request is pending platform eligibility; effective runtime remains Function at 1gb.');
125
+ }
56
126
  const base = {
57
127
  type: 'api',
58
128
  pattern: route.urlPattern,
@@ -63,6 +133,7 @@ function inspectApiRoute(route) {
63
133
  methods: route.methods,
64
134
  schedule,
65
135
  hasWebsocket: Boolean(route.hasWebsocket),
136
+ ...details,
66
137
  warnings,
67
138
  nextCommand: route.kind === 'task'
68
139
  ? `vura tasks run ${taskNameFromPattern(route.urlPattern)}`
@@ -85,6 +156,7 @@ function inspectApiRoute(route) {
85
156
  methods: route.methods,
86
157
  schedule,
87
158
  hasWebsocket: false,
159
+ ...details,
88
160
  warnings: [],
89
161
  nextCommand: 'vura tasks list',
90
162
  },
@@ -140,6 +212,17 @@ export function adviseRuntime(manifest) {
140
212
  const currentProfile = profileForApiRoute(route);
141
213
  const schedule = configString(route.config, 'schedule');
142
214
  const timeout = configNumber(route.config, 'timeout');
215
+ const details = computeDetails(route);
216
+ if (details.requestedComputeClass === 'edge') {
217
+ advice.push({
218
+ pattern: route.urlPattern,
219
+ type: 'api',
220
+ currentProfile,
221
+ recommendation: currentProfile,
222
+ severity: 'warn',
223
+ reason: 'Edge request is pending measured platform eligibility; deploys continue on Function at 1gb until approved.',
224
+ });
225
+ }
143
226
  if (route.hasWebsocket) {
144
227
  advice.push({
145
228
  pattern: route.urlPattern,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celsian/vura-cli",
3
- "version": "0.5.7",
3
+ "version": "0.5.10",
4
4
  "description": "Vura CLI — build and deploy full-stack apps",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,12 +15,12 @@
15
15
  "!dist/**/*.map"
16
16
  ],
17
17
  "dependencies": {
18
- "@celsian/vura-core": "0.5.7",
18
+ "@celsian/vura-core": "0.5.10",
19
19
  "esbuild": "^0.28.1",
20
20
  "what-framework": "^0.11.1"
21
21
  },
22
22
  "peerDependencies": {
23
- "@celsian/vura-adapter-vura": "0.5.7",
23
+ "@celsian/vura-adapter-vura": "0.5.10",
24
24
  "ws": "^8.0.0"
25
25
  },
26
26
  "peerDependenciesMeta": {
@@ -32,7 +32,7 @@
32
32
  }
33
33
  },
34
34
  "devDependencies": {
35
- "@celsian/vura-adapter-vura": "0.5.7",
35
+ "@celsian/vura-adapter-vura": "0.5.10",
36
36
  "@types/ws": "^8.18.1",
37
37
  "ws": "^8.21.0"
38
38
  },