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 +1 -1
- package/src/cli.js +23 -17
- package/src/shadow-dos.js +16 -16
- package/src/string-shim.js +35 -34
package/package.json
CHANGED
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
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
} catch
|
|
774
|
-
|
|
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
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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
|
-
|
|
807
|
+
console.log('\n\x1b[32m✔ Cloudflare D1 Remote Pipeline successfully upgraded.\x1b[0m');
|
|
802
808
|
} else {
|
|
803
|
-
|
|
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
|
|
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
|
-
|
|
123
|
-
|
|
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
|
}
|
package/src/string-shim.js
CHANGED
|
@@ -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) =>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|