vmlive 1.0.19 → 1.0.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vmlive",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Local development VM for custom Serverless PaaS",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -403,9 +403,15 @@ const runDev = async () => {
403
403
  const workspaceId = config.workspaceId || "ws_local";
404
404
  const projectId = config.projectId || "prj_local";
405
405
 
406
+ const configPath = path.join(os.homedir(), '.vm-config.json');
407
+ let jwtToken = process.env.VM_API_TOKEN;
408
+ if (!jwtToken && fs.existsSync(configPath)) {
409
+ jwtToken = JSON.parse(fs.readFileSync(configPath, 'utf-8')).token;
410
+ }
411
+
406
412
  config.functions.forEach(fn => {
407
413
  const relativeTarget = `./${fn.name}-out.mjs`;
408
- fs.writeFileSync(path.join(WORK_DIR, `${fn.name}-shim.mjs`), generateShim(relativeTarget, workspaceId, projectId, fn.name));
414
+ fs.writeFileSync(path.join(WORK_DIR, `${fn.name}-shim.mjs`), generateShim(relativeTarget, workspaceId, projectId, fn.name, jwtToken));
409
415
  });
410
416
 
411
417
  const builder = await esbuild.context({
@@ -761,17 +767,17 @@ const runDb = async () => {
761
767
 
762
768
  if (envArg === 'local') {
763
769
  console.log('\x1b[90mSyncing Local V8 Emulator D1...\x1b[0m');
764
- const mf = new Emulator({
770
+ const mf = new Emulator({
765
771
  modules: true,
766
- script: 'export default { fetch: () => new Response("ok") }',
767
- d1Databases: ["DB"]
772
+ script: 'export default { fetch: () => new Response("ok") }',
773
+ d1Databases: ["DB"]
768
774
  });
769
775
  try {
770
- const db = await mf.getD1Database("DB");
771
- await db.exec(combinedSql);
772
- console.log('\x1b[32m✔ Local Database push successful.\x1b[0m');
773
- } catch (e) {
774
- console.error('\n\x1b[31m❌ Local execute failed:\x1b[0m', e.message);
776
+ const db = await mf.getD1Database("DB");
777
+ await db.exec(combinedSql);
778
+ console.log('\x1b[32m✔ Local Database push successful.\x1b[0m');
779
+ } catch(e) {
780
+ console.error('\n\x1b[31m❌ Local execute failed:\x1b[0m', e.message);
775
781
  }
776
782
  await mf.dispose();
777
783
  } else {
@@ -789,18 +795,18 @@ const runDb = async () => {
789
795
 
790
796
  const { projectId } = vmConf;
791
797
  console.log(`\x1b[90mSyncing Cloudflare Edge D1 [${projectId}]...\x1b[0m`);
792
-
798
+
793
799
  try {
794
800
  const res = await fetch(`${GATEKEEPER_URL}/api/projects/${projectId}/database/migrate`, {
795
- method: 'POST',
796
- headers: { 'Authorization': `Bearer ${jwtToken}`, 'Content-Type': 'application/json' },
797
- body: JSON.stringify({ sql: combinedSql })
801
+ method: 'POST',
802
+ headers: { 'Authorization': `Bearer ${jwtToken}`, 'Content-Type': 'application/json' },
803
+ body: JSON.stringify({ sql: combinedSql })
798
804
  });
799
-
805
+
800
806
  if (res.ok) {
801
- console.log('\n\x1b[32m✔ Cloudflare D1 Remote Pipeline successfully upgraded.\x1b[0m');
807
+ console.log('\n\x1b[32m✔ Cloudflare D1 Remote Pipeline successfully upgraded.\x1b[0m');
802
808
  } else {
803
- console.error('\n\x1b[31m❌ Remote Migration Error:\x1b[0m', res.status, await res.text());
809
+ console.error('\n\x1b[31m❌ Remote Migration Error:\x1b[0m', res.status, await res.text());
804
810
  }
805
811
  } catch (err) {
806
812
  console.error('\n\x1b[31m❌ Network Failure:\x1b[0m', err.message);
@@ -836,7 +842,7 @@ const main = async () => {
836
842
  console.log(' \x1b[32mlogin\x1b[0m Authenticate your local environment securely via Stripe/OAuth');
837
843
  console.log(' \x1b[32mdeploy\x1b[0m Upload your footprint to the vm.live edge platform');
838
844
  console.log(' \x1b[32mdb\x1b[0m Manage Cloudflare D1 schema migrations (`db push local|remote`)');
839
- console.log(' \x1b[32mllm\x1b[0m Download the official AI Edge Context Rules for agentic workflows');
845
+ console.log(' \x1b[32mllm\x1b[0m Download the official AI Context Rules for agentic workflows');
840
846
  console.log(' \x1b[32mupdate\x1b[0m Upgrade this CLI engine to the latest published version');
841
847
  console.log(' \x1b[32mwhich\x1b[0m Display the currently active CLI version');
842
848
  console.log('');
package/src/shadow-dos.js CHANGED
@@ -7,13 +7,13 @@ export class LocalTaskManagerDO extends DurableObject {
7
7
 
8
8
  async fetch(request) {
9
9
  const url = new URL(request.url);
10
-
10
+
11
11
  if (request.method === "POST" && url.pathname === "/.internal/tasks/schedule") {
12
12
  const body = await request.json();
13
13
  const { functionSlug, executeAt, name, payload, workspaceId, projectId, interval } = body;
14
-
14
+
15
15
  const taskId = interval ? `cron:${functionSlug}:${name}` : crypto.randomUUID();
16
-
16
+
17
17
  const taskRecord = {
18
18
  id: taskId,
19
19
  function_slug: functionSlug,
@@ -30,7 +30,7 @@ export class LocalTaskManagerDO extends DurableObject {
30
30
  await this.ctx.storage.put(taskId, taskRecord);
31
31
  // We set the native Miniflare alarm
32
32
  await this.ctx.storage.setAlarm(executeAt);
33
-
33
+
34
34
  return Response.json({ success: true, id: taskId });
35
35
  }
36
36
  return new Response("Not Found", { status: 404 });
@@ -39,13 +39,13 @@ export class LocalTaskManagerDO extends DurableObject {
39
39
  async alarm() {
40
40
  const now = Date.now();
41
41
  const MapTasks = await this.ctx.storage.list();
42
-
42
+
43
43
  for (const [id, task] of MapTasks) {
44
44
  if (task.execute_at <= now && task.status !== 'completed') {
45
45
  try {
46
46
  // Structurally bypass network by resolving back through Miniflare host headers natively
47
47
  const invokeUrl = `http://127.0.0.1:${this.env.PORT || 8787}/.internal/tasks/dispatch`;
48
-
48
+
49
49
  const res = await fetch(invokeUrl, {
50
50
  method: 'POST',
51
51
  headers: {
@@ -72,7 +72,7 @@ export class LocalTaskManagerDO extends DurableObject {
72
72
  if (unit === 's') addMs = val * 1000;
73
73
  else if (unit === 'm') addMs = val * 60000;
74
74
  else if (unit === 'h') addMs = val * 3600000;
75
-
75
+
76
76
  task.execute_at = now + addMs;
77
77
  await this.ctx.storage.put(id, task);
78
78
  await this.ctx.storage.setAlarm(task.execute_at);
@@ -94,10 +94,10 @@ export class LocalChannelRoomDO extends DurableObject {
94
94
  constructor(ctx, env) {
95
95
  super(ctx, env);
96
96
  }
97
-
97
+
98
98
  async fetch(request) {
99
99
  const url = new URL(request.url);
100
-
100
+
101
101
  if (request.method === "POST" && url.pathname === "/broadcast") {
102
102
  const body = await request.json();
103
103
  const websockets = this.ctx.getWebSockets();
@@ -106,25 +106,25 @@ export class LocalChannelRoomDO extends DurableObject {
106
106
  }
107
107
  return new Response("OK");
108
108
  }
109
-
109
+
110
110
  if (request.headers.get("Upgrade") === "websocket") {
111
111
  const pair = new WebSocketPair();
112
112
  const [client, server] = Object.values(pair);
113
113
  this.ctx.acceptWebSocket(server);
114
114
  return new Response(null, { status: 101, webSocket: client });
115
115
  }
116
-
116
+
117
117
  return new Response("Not Found", { status: 404 });
118
118
  }
119
-
119
+
120
120
  webSocketMessage(ws, message) {
121
121
  this.ctx.getWebSockets().forEach((sock) => {
122
- if (sock !== ws) {
123
- sock.send(message);
124
- }
122
+ if (sock !== ws) {
123
+ sock.send(message);
124
+ }
125
125
  });
126
126
  }
127
-
127
+
128
128
  webSocketClose(ws, code, reason, wasClean) {
129
129
  ws.close(code, reason);
130
130
  }
@@ -1,4 +1,4 @@
1
- export const generateShim = (outFileUrl, workspaceId, projectId, functionSlug) => `
1
+ export const generateShim = (outFileUrl, workspaceId, projectId, functionSlug, jwtToken) => `
2
2
  import * as UserCode from '${outFileUrl}';
3
3
 
4
4
  export default {
@@ -109,40 +109,41 @@ export default {
109
109
  }
110
110
  };
111
111
 
112
- const createRpcProxy = (serviceName) => new Proxy({}, {
113
- get: (_, method) => async (...args) => {
114
- console.log(\`\\x1b[35m[\${env.PROJECT_ID || '${projectId}'}]\\x1b[0m ☁️ \${serviceName}.\${method} proxied to vm.live edge\`);
115
-
116
- let body;
117
- let contentType = "application/json";
118
-
119
- if (args.length === 1 && args[0] instanceof ArrayBuffer) {
120
- body = args[0];
121
- contentType = "application/octet-stream";
122
- } else {
123
- body = JSON.stringify({ args });
124
- }
125
-
126
- const req = new Request(\`https://rpc.vm.codes/.internal/rpc/\${serviceName.toLowerCase()}/\${method}\`, {
127
- method: "POST",
128
- headers: {
129
- "Authorization": \`Bearer \${safeEnv.BILLING_TOKEN || 'local'}\`,
130
- "Content-Type": contentType
131
- },
132
- body
133
- });
134
- const res = await fetch(req);
135
- if (!res.ok) throw new Error(\`vm.live RPC Error: \${await res.text()}\`);
136
-
137
- const isStream = res.headers.get("Content-Type") === "text/event-stream";
138
- if (isStream) return res.body;
112
+ const createRpcProxy = (serviceName) => {
113
+ return new Proxy({}, {
114
+ get: (target, method) => {
115
+ return async (...args) => {
116
+ let body = null;
117
+ let contentType = "application/json";
118
+ if (serviceName === "AI" && method === "transcribe") {
119
+ body = args[0];
120
+ contentType = "application/octet-stream";
121
+ } else {
122
+ body = JSON.stringify({ args });
123
+ }
139
124
 
140
- const isBuffer = res.headers.get("Content-Type") === "application/octet-stream" || res.headers.get("Content-Type")?.includes("image/");
141
- if (isBuffer) return await res.arrayBuffer();
142
-
143
- return await res.json();
144
- }
145
- });
125
+ const req = new Request(\`https://rpc.vm.codes/.internal/rpc/\${serviceName.toLowerCase()}/\${method}\`, {
126
+ method: "POST",
127
+ headers: {
128
+ "Authorization": "Bearer \${jwtToken || 'local'}",
129
+ "Content-Type": contentType,
130
+ "x-workspace-id": "\${workspaceId}",
131
+ "x-project-id": "\${projectId}"
132
+ },
133
+ body
134
+ });
135
+
136
+ const res = await fetch(req);
137
+ if (!res.ok) throw new Error(\`vm.live RPC Error: \${await res.text()}\`);
138
+
139
+ if (res.headers.get("Content-Type")?.includes("text/event-stream")) {
140
+ return res.body;
141
+ }
142
+ return await res.json();
143
+ }
144
+ }
145
+ });
146
+ };
146
147
 
147
148
  const AI = createRpcProxy("AI");
148
149