run402 1.21.0 → 1.22.0
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/lib/functions.mjs +95 -3
- package/package.json +1 -1
package/lib/functions.mjs
CHANGED
|
@@ -12,7 +12,10 @@ Subcommands:
|
|
|
12
12
|
Deploy a function to a project
|
|
13
13
|
invoke <id> <name> [--method <M>] [--body <json>]
|
|
14
14
|
Invoke a deployed function
|
|
15
|
-
logs <id> <name> [--tail <n>]
|
|
15
|
+
logs <id> <name> [--tail <n>] [--since <ts>] [--follow]
|
|
16
|
+
Get function logs
|
|
17
|
+
update <id> <name> [--schedule <cron>] [--schedule-remove] [--timeout <s>] [--memory <mb>]
|
|
18
|
+
Update function schedule or config without re-deploying
|
|
16
19
|
list <id> List all functions for a project
|
|
17
20
|
delete <id> <name> Delete a function
|
|
18
21
|
|
|
@@ -22,6 +25,11 @@ Examples:
|
|
|
22
25
|
run402 functions deploy abc123 send-reminders --file remind.ts --schedule '' # remove schedule
|
|
23
26
|
run402 functions invoke abc123 stripe-webhook --body '{"event":"test"}'
|
|
24
27
|
run402 functions logs abc123 stripe-webhook --tail 100
|
|
28
|
+
run402 functions logs abc123 stripe-webhook --since 2026-03-29T14:00:00Z
|
|
29
|
+
run402 functions logs abc123 stripe-webhook --follow
|
|
30
|
+
run402 functions update abc123 send-reminders --schedule '0 */4 * * *'
|
|
31
|
+
run402 functions update abc123 send-reminders --schedule-remove
|
|
32
|
+
run402 functions update abc123 my-func --timeout 15 --memory 256
|
|
25
33
|
run402 functions list abc123
|
|
26
34
|
run402 functions delete abc123 stripe-webhook
|
|
27
35
|
|
|
@@ -84,11 +92,94 @@ async function invoke(projectId, name, args) {
|
|
|
84
92
|
async function logs(projectId, name, args) {
|
|
85
93
|
const p = findProject(projectId);
|
|
86
94
|
let tail = 50;
|
|
95
|
+
let since = undefined;
|
|
96
|
+
let follow = false;
|
|
87
97
|
for (let i = 0; i < args.length; i++) {
|
|
88
98
|
if (args[i] === "--tail" && args[i + 1]) tail = parseInt(args[++i]);
|
|
99
|
+
if (args[i] === "--since" && args[i + 1]) since = args[++i];
|
|
100
|
+
if (args[i] === "--follow") follow = true;
|
|
89
101
|
}
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
|
|
103
|
+
// Parse since: accept ISO string or epoch ms
|
|
104
|
+
let sinceMs = undefined;
|
|
105
|
+
if (since !== undefined) {
|
|
106
|
+
const parsed = Number(since);
|
|
107
|
+
sinceMs = Number.isNaN(parsed) ? new Date(since).getTime() : parsed;
|
|
108
|
+
if (Number.isNaN(sinceMs)) { console.error(JSON.stringify({ status: "error", message: `Invalid --since value: ${since}` })); process.exit(1); }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const fetchLogs = async () => {
|
|
112
|
+
let url = `${API}/projects/v1/admin/${projectId}/functions/${encodeURIComponent(name)}/logs?tail=${tail}`;
|
|
113
|
+
if (sinceMs !== undefined) url += `&since=${sinceMs}`;
|
|
114
|
+
const res = await fetch(url, { headers: { "Authorization": `Bearer ${p.service_key}` } });
|
|
115
|
+
const data = await res.json();
|
|
116
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
117
|
+
return data.logs || [];
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (!follow) {
|
|
121
|
+
const entries = await fetchLogs();
|
|
122
|
+
console.log(JSON.stringify({ logs: entries }, null, 2));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Follow mode: poll every 3s, print new entries
|
|
127
|
+
let running = true;
|
|
128
|
+
process.on("SIGINT", () => { running = false; });
|
|
129
|
+
|
|
130
|
+
// Initial fetch
|
|
131
|
+
const initial = await fetchLogs();
|
|
132
|
+
for (const entry of initial) {
|
|
133
|
+
console.log(`[${entry.timestamp}] ${entry.message}`);
|
|
134
|
+
}
|
|
135
|
+
if (initial.length > 0) {
|
|
136
|
+
sinceMs = new Date(initial[initial.length - 1].timestamp).getTime() + 1;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
while (running) {
|
|
140
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
141
|
+
if (!running) break;
|
|
142
|
+
const entries = await fetchLogs();
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
console.log(`[${entry.timestamp}] ${entry.message}`);
|
|
145
|
+
}
|
|
146
|
+
if (entries.length > 0) {
|
|
147
|
+
sinceMs = new Date(entries[entries.length - 1].timestamp).getTime() + 1;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function update(projectId, name, args) {
|
|
153
|
+
const p = findProject(projectId);
|
|
154
|
+
let schedule = undefined;
|
|
155
|
+
let scheduleRemove = false;
|
|
156
|
+
let timeout = undefined;
|
|
157
|
+
let memory = undefined;
|
|
158
|
+
for (let i = 0; i < args.length; i++) {
|
|
159
|
+
if (args[i] === "--schedule" && i + 1 < args.length) schedule = args[++i];
|
|
160
|
+
if (args[i] === "--schedule-remove") scheduleRemove = true;
|
|
161
|
+
if (args[i] === "--timeout" && args[i + 1]) timeout = parseInt(args[++i]);
|
|
162
|
+
if (args[i] === "--memory" && args[i + 1]) memory = parseInt(args[++i]);
|
|
163
|
+
}
|
|
164
|
+
const body = {};
|
|
165
|
+
if (scheduleRemove || schedule === "") {
|
|
166
|
+
body.schedule = null;
|
|
167
|
+
} else if (schedule !== undefined) {
|
|
168
|
+
body.schedule = schedule;
|
|
169
|
+
}
|
|
170
|
+
if (timeout !== undefined || memory !== undefined) {
|
|
171
|
+
body.config = {};
|
|
172
|
+
if (timeout !== undefined) body.config.timeout = timeout;
|
|
173
|
+
if (memory !== undefined) body.config.memory = memory;
|
|
174
|
+
}
|
|
175
|
+
if (Object.keys(body).length === 0) {
|
|
176
|
+
console.error(JSON.stringify({ status: "error", message: "Provide at least one of: --schedule, --schedule-remove, --timeout, --memory" }));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const res = await fetch(`${API}/projects/v1/admin/${projectId}/functions/${encodeURIComponent(name)}`, {
|
|
180
|
+
method: "PATCH",
|
|
181
|
+
headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "application/json" },
|
|
182
|
+
body: JSON.stringify(body),
|
|
92
183
|
});
|
|
93
184
|
const data = await res.json();
|
|
94
185
|
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
@@ -125,6 +216,7 @@ export async function run(sub, args) {
|
|
|
125
216
|
case "deploy": await deploy(args[0], args[1], args.slice(2)); break;
|
|
126
217
|
case "invoke": await invoke(args[0], args[1], args.slice(2)); break;
|
|
127
218
|
case "logs": await logs(args[0], args[1], args.slice(2)); break;
|
|
219
|
+
case "update": await update(args[0], args[1], args.slice(2)); break;
|
|
128
220
|
case "list": await list(args[0]); break;
|
|
129
221
|
case "delete": await deleteFunction(args[0], args[1]); break;
|
|
130
222
|
default:
|
package/package.json
CHANGED