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.
Files changed (2) hide show
  1. package/lib/functions.mjs +95 -3
  2. 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>] Get function logs
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
- const res = await fetch(`${API}/projects/v1/admin/${projectId}/functions/${encodeURIComponent(name)}/logs?tail=${tail}`, {
91
- headers: { "Authorization": `Bearer ${p.service_key}` },
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "CLI for Run402 — provision Postgres databases, deploy static sites, generate images, and manage wallets via x402 and MPP micropayments.",
5
5
  "type": "module",
6
6
  "bin": {