run402 1.38.0 → 1.40.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/agent.mjs +16 -15
- package/lib/ai.mjs +21 -33
- package/lib/allowance.mjs +41 -16
- package/lib/apps.mjs +71 -79
- package/lib/auth.mjs +27 -66
- package/lib/billing.mjs +37 -47
- package/lib/blob.mjs +37 -35
- package/lib/contracts.mjs +82 -193
- package/lib/domains.mjs +30 -34
- package/lib/email.mjs +94 -344
- package/lib/functions.mjs +77 -79
- package/lib/image.mjs +13 -14
- package/lib/message.mjs +11 -10
- package/lib/projects.mjs +121 -90
- package/lib/sdk-errors.mjs +45 -0
- package/lib/sdk.mjs +14 -0
- package/lib/secrets.mjs +18 -26
- package/lib/sender-domain.mjs +28 -45
- package/lib/service.mjs +16 -19
- package/lib/sites.mjs +24 -20
- package/lib/subdomains.mjs +22 -29
- package/lib/tier.mjs +10 -24
- package/lib/webhooks.mjs +32 -129
- package/package.json +1 -1
- package/lib/paid-fetch.mjs +0 -107
package/lib/functions.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { findProject, API } from "./config.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { getSdk } from "./sdk.mjs";
|
|
4
|
+
import { reportSdkError } from "./sdk-errors.mjs";
|
|
4
5
|
|
|
5
6
|
const HELP = `run402 functions — Manage serverless functions
|
|
6
7
|
|
|
@@ -128,7 +129,6 @@ Examples:
|
|
|
128
129
|
};
|
|
129
130
|
|
|
130
131
|
async function deploy(projectId, name, args) {
|
|
131
|
-
const p = findProject(projectId);
|
|
132
132
|
const opts = { file: null, timeout: undefined, memory: undefined, deps: undefined, schedule: undefined };
|
|
133
133
|
for (let i = 0; i < args.length; i++) {
|
|
134
134
|
if (args[i] === "--file" && args[i + 1]) opts.file = args[++i];
|
|
@@ -139,47 +139,48 @@ async function deploy(projectId, name, args) {
|
|
|
139
139
|
}
|
|
140
140
|
if (!opts.file) { console.error(JSON.stringify({ status: "error", message: "Missing --file <file>" })); process.exit(1); }
|
|
141
141
|
const code = readFileSync(opts.file, "utf-8");
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (opts.timeout
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
142
|
+
|
|
143
|
+
const deployOpts = { name, code };
|
|
144
|
+
if (opts.timeout !== undefined || opts.memory !== undefined) {
|
|
145
|
+
deployOpts.config = {};
|
|
146
|
+
if (opts.timeout !== undefined) deployOpts.config.timeout = opts.timeout;
|
|
147
|
+
if (opts.memory !== undefined) deployOpts.config.memory = opts.memory;
|
|
148
|
+
}
|
|
149
|
+
if (opts.deps !== undefined) deployOpts.deps = opts.deps;
|
|
150
|
+
if (opts.schedule !== undefined) deployOpts.schedule = opts.schedule === "" ? null : opts.schedule;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const data = await getSdk().functions.deploy(projectId, deployOpts);
|
|
154
|
+
console.log(JSON.stringify(data, null, 2));
|
|
155
|
+
} catch (err) {
|
|
156
|
+
reportSdkError(err);
|
|
157
|
+
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
async function invoke(projectId, name, args) {
|
|
161
|
-
const p = findProject(projectId);
|
|
162
161
|
const opts = { method: "POST", body: undefined };
|
|
163
162
|
for (let i = 0; i < args.length; i++) {
|
|
164
163
|
if (args[i] === "--method" && args[i + 1]) opts.method = args[++i];
|
|
165
164
|
if (args[i] === "--body" && args[i + 1]) opts.body = args[++i];
|
|
166
165
|
}
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
166
|
+
const invokeOpts = { method: opts.method };
|
|
167
|
+
if (opts.body !== undefined && opts.method !== "GET" && opts.method !== "HEAD") {
|
|
168
|
+
invokeOpts.body = opts.body;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const result = await getSdk().functions.invoke(projectId, name, invokeOpts);
|
|
172
|
+
const body = result.body;
|
|
173
|
+
if (typeof body === "string") {
|
|
174
|
+
process.stdout.write(body + "\n");
|
|
175
|
+
} else {
|
|
176
|
+
console.log(JSON.stringify(body, null, 2));
|
|
177
|
+
}
|
|
178
|
+
} catch (err) {
|
|
179
|
+
reportSdkError(err);
|
|
174
180
|
}
|
|
175
|
-
const res = await fetch(`${API}/functions/v1/${name}`, fetchOpts);
|
|
176
|
-
const text = await res.text();
|
|
177
|
-
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, body: text })); process.exit(1); }
|
|
178
|
-
try { console.log(JSON.stringify(JSON.parse(text), null, 2)); } catch { process.stdout.write(text + "\n"); }
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
async function logs(projectId, name, args) {
|
|
182
|
-
const p = findProject(projectId);
|
|
183
184
|
let tail = 50;
|
|
184
185
|
let since = undefined;
|
|
185
186
|
let follow = false;
|
|
@@ -189,21 +190,28 @@ async function logs(projectId, name, args) {
|
|
|
189
190
|
if (args[i] === "--follow") follow = true;
|
|
190
191
|
}
|
|
191
192
|
|
|
192
|
-
// Parse since: accept ISO string or epoch ms
|
|
193
|
-
|
|
193
|
+
// Parse since: accept ISO string or epoch ms — keep CLI-side validation
|
|
194
|
+
// so a bad `--since` errors with a clear message rather than silently
|
|
195
|
+
// being dropped by the SDK.
|
|
196
|
+
let sinceIso = undefined;
|
|
194
197
|
if (since !== undefined) {
|
|
195
198
|
const parsed = Number(since);
|
|
196
|
-
|
|
197
|
-
if (Number.isNaN(
|
|
199
|
+
const ms = Number.isNaN(parsed) ? new Date(since).getTime() : parsed;
|
|
200
|
+
if (Number.isNaN(ms)) { console.error(JSON.stringify({ status: "error", message: `Invalid --since value: ${since}` })); process.exit(1); }
|
|
201
|
+
sinceIso = new Date(ms).toISOString();
|
|
198
202
|
}
|
|
199
203
|
|
|
200
204
|
const fetchLogs = async () => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
try {
|
|
206
|
+
const data = await getSdk().functions.logs(projectId, name, {
|
|
207
|
+
tail,
|
|
208
|
+
since: sinceIso,
|
|
209
|
+
});
|
|
210
|
+
return data.logs || [];
|
|
211
|
+
} catch (err) {
|
|
212
|
+
reportSdkError(err);
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
207
215
|
};
|
|
208
216
|
|
|
209
217
|
if (!follow) {
|
|
@@ -212,17 +220,16 @@ async function logs(projectId, name, args) {
|
|
|
212
220
|
return;
|
|
213
221
|
}
|
|
214
222
|
|
|
215
|
-
// Follow mode: poll every 3s, print new entries
|
|
223
|
+
// Follow mode: poll every 3s, print new entries.
|
|
216
224
|
let running = true;
|
|
217
225
|
process.on("SIGINT", () => { running = false; });
|
|
218
226
|
|
|
219
|
-
// Initial fetch
|
|
220
227
|
const initial = await fetchLogs();
|
|
221
228
|
for (const entry of initial) {
|
|
222
229
|
console.log(`[${entry.timestamp}] ${entry.message}`);
|
|
223
230
|
}
|
|
224
231
|
if (initial.length > 0) {
|
|
225
|
-
|
|
232
|
+
sinceIso = new Date(new Date(initial[initial.length - 1].timestamp).getTime() + 1).toISOString();
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
while (running) {
|
|
@@ -233,13 +240,12 @@ async function logs(projectId, name, args) {
|
|
|
233
240
|
console.log(`[${entry.timestamp}] ${entry.message}`);
|
|
234
241
|
}
|
|
235
242
|
if (entries.length > 0) {
|
|
236
|
-
|
|
243
|
+
sinceIso = new Date(new Date(entries[entries.length - 1].timestamp).getTime() + 1).toISOString();
|
|
237
244
|
}
|
|
238
245
|
}
|
|
239
246
|
}
|
|
240
247
|
|
|
241
248
|
async function update(projectId, name, args) {
|
|
242
|
-
const p = findProject(projectId);
|
|
243
249
|
let schedule = undefined;
|
|
244
250
|
let scheduleRemove = false;
|
|
245
251
|
let timeout = undefined;
|
|
@@ -250,52 +256,44 @@ async function update(projectId, name, args) {
|
|
|
250
256
|
if (args[i] === "--timeout" && args[i + 1]) timeout = parseInt(args[++i]);
|
|
251
257
|
if (args[i] === "--memory" && args[i + 1]) memory = parseInt(args[++i]);
|
|
252
258
|
}
|
|
253
|
-
|
|
259
|
+
|
|
260
|
+
const updateOpts = {};
|
|
254
261
|
if (scheduleRemove || schedule === "") {
|
|
255
|
-
|
|
262
|
+
updateOpts.schedule = null;
|
|
256
263
|
} else if (schedule !== undefined) {
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
if (timeout !== undefined || memory !== undefined) {
|
|
260
|
-
body.config = {};
|
|
261
|
-
if (timeout !== undefined) body.config.timeout = timeout;
|
|
262
|
-
if (memory !== undefined) body.config.memory = memory;
|
|
264
|
+
updateOpts.schedule = schedule;
|
|
263
265
|
}
|
|
264
|
-
if (
|
|
266
|
+
if (timeout !== undefined) updateOpts.timeout = timeout;
|
|
267
|
+
if (memory !== undefined) updateOpts.memory = memory;
|
|
268
|
+
|
|
269
|
+
if (Object.keys(updateOpts).length === 0) {
|
|
265
270
|
console.error(JSON.stringify({ status: "error", message: "Provide at least one of: --schedule, --schedule-remove, --timeout, --memory" }));
|
|
266
271
|
process.exit(1);
|
|
267
272
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
console.log(JSON.stringify(data, null, 2));
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const data = await getSdk().functions.update(projectId, name, updateOpts);
|
|
276
|
+
console.log(JSON.stringify(data, null, 2));
|
|
277
|
+
} catch (err) {
|
|
278
|
+
reportSdkError(err);
|
|
279
|
+
}
|
|
276
280
|
}
|
|
277
281
|
|
|
278
282
|
async function list(projectId) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
console.log(JSON.stringify(data, null, 2));
|
|
283
|
+
try {
|
|
284
|
+
const data = await getSdk().functions.list(projectId);
|
|
285
|
+
console.log(JSON.stringify(data, null, 2));
|
|
286
|
+
} catch (err) {
|
|
287
|
+
reportSdkError(err);
|
|
288
|
+
}
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
async function deleteFunction(projectId, name) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
method: "DELETE",
|
|
292
|
-
headers: { "Authorization": `Bearer ${p.service_key}` },
|
|
293
|
-
});
|
|
294
|
-
if (res.status === 204 || res.ok) {
|
|
292
|
+
try {
|
|
293
|
+
await getSdk().functions.delete(projectId, name);
|
|
295
294
|
console.log(JSON.stringify({ status: "ok", message: `Function '${name}' deleted.` }));
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1);
|
|
295
|
+
} catch (err) {
|
|
296
|
+
reportSdkError(err);
|
|
299
297
|
}
|
|
300
298
|
}
|
|
301
299
|
|
package/lib/image.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writeFileSync } from "fs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { getSdk } from "./sdk.mjs";
|
|
3
|
+
import { reportSdkError } from "./sdk-errors.mjs";
|
|
4
4
|
|
|
5
5
|
const HELP = `run402 image — Generate AI images via x402 micropayments
|
|
6
6
|
|
|
@@ -51,17 +51,16 @@ export async function run(sub, args) {
|
|
|
51
51
|
|
|
52
52
|
if (!opts.prompt) { console.error(JSON.stringify({ status: "error", message: "Prompt required. Usage: run402 image generate \"your prompt\"" })); process.exit(1); }
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
console.log(JSON.stringify({ status: "ok", aspect: data.aspect, content_type: data.content_type, image: data.image }));
|
|
54
|
+
try {
|
|
55
|
+
const data = await getSdk().ai.generateImage({ prompt: opts.prompt, aspect: opts.aspect });
|
|
56
|
+
if (opts.output) {
|
|
57
|
+
const buf = Buffer.from(data.image, "base64");
|
|
58
|
+
writeFileSync(opts.output, buf);
|
|
59
|
+
console.log(JSON.stringify({ status: "ok", file: opts.output, size: buf.length, aspect: data.aspect }));
|
|
60
|
+
} else {
|
|
61
|
+
console.log(JSON.stringify({ status: "ok", aspect: data.aspect, content_type: data.content_type, image: data.image }));
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
reportSdkError(err);
|
|
66
65
|
}
|
|
67
66
|
}
|
package/lib/message.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { allowanceAuthHeaders } from "./config.mjs";
|
|
2
|
+
import { getSdk } from "./sdk.mjs";
|
|
3
|
+
import { reportSdkError } from "./sdk-errors.mjs";
|
|
2
4
|
|
|
3
5
|
const HELP = `run402 message — Send messages to Run402 developers
|
|
4
6
|
|
|
@@ -15,16 +17,15 @@ Examples:
|
|
|
15
17
|
|
|
16
18
|
async function send(text) {
|
|
17
19
|
if (!text) { console.error(JSON.stringify({ status: "error", message: "Missing message text" })); process.exit(1); }
|
|
18
|
-
|
|
20
|
+
// Preserve the aggressive early exit when no allowance is configured.
|
|
21
|
+
allowanceAuthHeaders("/message/v1");
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
27
|
-
console.log(JSON.stringify(data, null, 2));
|
|
23
|
+
try {
|
|
24
|
+
await getSdk().admin.sendMessage(text);
|
|
25
|
+
console.log(JSON.stringify({ status: "ok", message: "Message sent to Run402 developers." }));
|
|
26
|
+
} catch (err) {
|
|
27
|
+
reportSdkError(err);
|
|
28
|
+
}
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export async function run(sub, args) {
|
package/lib/projects.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
|
-
import { findProject, loadKeyStore,
|
|
2
|
+
import { findProject, loadKeyStore, API, allowanceAuthHeaders, resolveProjectId } from "./config.mjs";
|
|
3
|
+
import { getSdk } from "./sdk.mjs";
|
|
4
|
+
import { reportSdkError } from "./sdk-errors.mjs";
|
|
3
5
|
|
|
4
6
|
const HELP = `run402 projects — Manage your deployed Run402 projects
|
|
5
7
|
|
|
@@ -17,7 +19,10 @@ Subcommands:
|
|
|
17
19
|
rest [id] <table> [params] Query a table via the REST API (PostgREST)
|
|
18
20
|
usage [id] Show compute/storage usage for a project
|
|
19
21
|
schema [id] Inspect the database schema
|
|
20
|
-
rls [id] <template> <tables_json>
|
|
22
|
+
rls [id] <template> <tables_json> ⚠ DEPRECATED (sunset 2026-05-23) - use 'apply-expose' instead
|
|
23
|
+
apply-expose [id] <manifest_json> Apply a declarative authorization manifest (supersedes 'rls')
|
|
24
|
+
apply-expose [id] --file <path> Apply a manifest from a JSON file
|
|
25
|
+
get-expose [id] Get the current authorization manifest
|
|
21
26
|
delete [id] Immediately and irreversibly delete a project (cascade purge) and remove from local state
|
|
22
27
|
pin [id] Pin a project (prevents expiry/GC)
|
|
23
28
|
promote-user [id] <email> Promote a user to project_admin role
|
|
@@ -37,6 +42,8 @@ Examples:
|
|
|
37
42
|
run402 projects usage abc123
|
|
38
43
|
run402 projects schema abc123
|
|
39
44
|
run402 projects rls abc123 public_read_authenticated_write '[{"table":"posts"}]'
|
|
45
|
+
run402 projects apply-expose abc123 --file manifest.json
|
|
46
|
+
run402 projects get-expose abc123
|
|
40
47
|
run402 projects keys abc123
|
|
41
48
|
run402 projects delete abc123
|
|
42
49
|
|
|
@@ -52,6 +59,14 @@ Notes:
|
|
|
52
59
|
public_read_authenticated_write anyone reads; any authenticated user writes any row
|
|
53
60
|
public_read_write_UNRESTRICTED fully open (anon_key writes); use 'run402 deploy' with a manifest
|
|
54
61
|
that includes "i_understand_this_is_unrestricted": true
|
|
62
|
+
- 'rls' is deprecated (sunset 2026-05-23) — migrate to 'apply-expose'.
|
|
63
|
+
The expose manifest declares the full authorization surface (tables, views,
|
|
64
|
+
RPCs) in one convergent call. Tables not listed with expose:true are dark
|
|
65
|
+
by default. Sample manifest:
|
|
66
|
+
{"version":"1",
|
|
67
|
+
"tables":[{"name":"posts","expose":true,"policy":"user_owns_rows","owner_column":"user_id","force_owner_on_insert":true}],
|
|
68
|
+
"views":[],
|
|
69
|
+
"rpcs":[]}
|
|
55
70
|
`;
|
|
56
71
|
|
|
57
72
|
const SUB_HELP = {
|
|
@@ -97,10 +112,12 @@ Examples:
|
|
|
97
112
|
};
|
|
98
113
|
|
|
99
114
|
async function quote() {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
try {
|
|
116
|
+
const data = await getSdk().projects.getQuote();
|
|
117
|
+
console.log(JSON.stringify(data, null, 2));
|
|
118
|
+
} catch (err) {
|
|
119
|
+
reportSdkError(err);
|
|
120
|
+
}
|
|
104
121
|
}
|
|
105
122
|
|
|
106
123
|
async function provision(args) {
|
|
@@ -109,60 +126,64 @@ async function provision(args) {
|
|
|
109
126
|
if (args[i] === "--tier" && args[i + 1]) opts.tier = args[++i];
|
|
110
127
|
if (args[i] === "--name" && args[i + 1]) opts.name = args[++i];
|
|
111
128
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
// 502/504/etc., which would otherwise crash res.json() with SyntaxError (GH-84).
|
|
122
|
-
const contentType = res.headers.get("content-type") || "";
|
|
123
|
-
let data = null;
|
|
124
|
-
let parseError = null;
|
|
125
|
-
let bodyText = null;
|
|
126
|
-
if (contentType.includes("application/json")) {
|
|
127
|
-
try {
|
|
128
|
-
data = await res.json();
|
|
129
|
-
} catch (e) {
|
|
130
|
-
parseError = e;
|
|
131
|
-
try { bodyText = await res.text(); } catch { bodyText = ""; }
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
try { bodyText = await res.text(); } catch { bodyText = ""; }
|
|
129
|
+
// Preserve the aggressive early exit when no allowance is configured —
|
|
130
|
+
// gives the user a more specific prompt than the SDK's 401/402 path.
|
|
131
|
+
allowanceAuthHeaders("/projects/v1");
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const data = await getSdk().projects.provision({ tier: opts.tier, name: opts.name });
|
|
135
|
+
console.log(JSON.stringify(data, null, 2));
|
|
136
|
+
} catch (err) {
|
|
137
|
+
reportSdkError(err);
|
|
135
138
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
console.error(JSON.stringify(err));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function rls(projectId, template, tablesJson) {
|
|
142
|
+
let tables;
|
|
143
|
+
try {
|
|
144
|
+
tables = JSON.parse(tablesJson);
|
|
145
|
+
} catch {
|
|
146
|
+
console.error(JSON.stringify({ status: "error", message: "Invalid JSON for tables argument" }));
|
|
146
147
|
process.exit(1);
|
|
147
148
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
154
|
-
setActiveProjectId(data.project_id);
|
|
149
|
+
try {
|
|
150
|
+
const data = await getSdk().projects.setupRls(projectId, { template, tables });
|
|
151
|
+
console.log(JSON.stringify(data, null, 2));
|
|
152
|
+
} catch (err) {
|
|
153
|
+
reportSdkError(err);
|
|
155
154
|
}
|
|
156
|
-
console.log(JSON.stringify(data, null, 2));
|
|
157
155
|
}
|
|
158
156
|
|
|
159
|
-
async function
|
|
157
|
+
async function applyExpose(projectId, args = []) {
|
|
160
158
|
const p = findProject(projectId);
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
let file = null;
|
|
160
|
+
let inline = null;
|
|
161
|
+
for (let i = 0; i < args.length; i++) {
|
|
162
|
+
if (args[i] === "--file" && args[i + 1]) { file = args[++i]; }
|
|
163
|
+
else if (!inline && !args[i].startsWith("--")) { inline = args[i]; }
|
|
164
|
+
}
|
|
165
|
+
const raw = file ? readFileSync(file, "utf-8") : inline;
|
|
166
|
+
if (!raw) {
|
|
167
|
+
console.error(JSON.stringify({ status: "error", message: "Missing manifest. Provide inline JSON or use --file <path>" }));
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
let manifest;
|
|
171
|
+
try { manifest = JSON.parse(raw); }
|
|
172
|
+
catch { console.error(JSON.stringify({ status: "error", message: "Invalid JSON for manifest" })); process.exit(1); }
|
|
173
|
+
const res = await fetch(`${API}/projects/v1/admin/${projectId}/expose`, {
|
|
163
174
|
method: "POST",
|
|
164
175
|
headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "application/json" },
|
|
165
|
-
body: JSON.stringify(
|
|
176
|
+
body: JSON.stringify(manifest),
|
|
177
|
+
});
|
|
178
|
+
const data = await res.json();
|
|
179
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
180
|
+
console.log(JSON.stringify(data, null, 2));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function getExpose(projectId) {
|
|
184
|
+
const p = findProject(projectId);
|
|
185
|
+
const res = await fetch(`${API}/projects/v1/admin/${projectId}/expose`, {
|
|
186
|
+
headers: { "Authorization": `Bearer ${p.service_key}` },
|
|
166
187
|
});
|
|
167
188
|
const data = await res.json();
|
|
168
189
|
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
@@ -178,20 +199,28 @@ async function list() {
|
|
|
178
199
|
}
|
|
179
200
|
|
|
180
201
|
async function info(projectId) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
202
|
+
try {
|
|
203
|
+
const data = await getSdk().projects.info(projectId);
|
|
204
|
+
console.log(JSON.stringify({
|
|
205
|
+
project_id: projectId,
|
|
206
|
+
rest_url: `${API}/rest/v1`,
|
|
207
|
+
anon_key: data.anon_key,
|
|
208
|
+
service_key: data.service_key,
|
|
209
|
+
site_url: data.site_url || null,
|
|
210
|
+
deployed_at: data.deployed_at || null,
|
|
211
|
+
}, null, 2));
|
|
212
|
+
} catch (err) {
|
|
213
|
+
reportSdkError(err);
|
|
214
|
+
}
|
|
190
215
|
}
|
|
191
216
|
|
|
192
217
|
async function keys(projectId) {
|
|
193
|
-
|
|
194
|
-
|
|
218
|
+
try {
|
|
219
|
+
const data = await getSdk().projects.keys(projectId);
|
|
220
|
+
console.log(JSON.stringify({ project_id: projectId, anon_key: data.anon_key, service_key: data.service_key }, null, 2));
|
|
221
|
+
} catch (err) {
|
|
222
|
+
reportSdkError(err);
|
|
223
|
+
}
|
|
195
224
|
}
|
|
196
225
|
|
|
197
226
|
async function sqlCmd(projectId, args = []) {
|
|
@@ -229,38 +258,41 @@ async function rest(projectId, table, queryParams) {
|
|
|
229
258
|
}
|
|
230
259
|
|
|
231
260
|
async function usage(projectId) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
261
|
+
try {
|
|
262
|
+
const data = await getSdk().projects.getUsage(projectId);
|
|
263
|
+
console.log(JSON.stringify(data, null, 2));
|
|
264
|
+
} catch (err) {
|
|
265
|
+
reportSdkError(err);
|
|
266
|
+
}
|
|
237
267
|
}
|
|
238
268
|
|
|
239
269
|
async function schema(projectId) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
270
|
+
try {
|
|
271
|
+
const data = await getSdk().projects.getSchema(projectId);
|
|
272
|
+
console.log(JSON.stringify(data, null, 2));
|
|
273
|
+
} catch (err) {
|
|
274
|
+
reportSdkError(err);
|
|
275
|
+
}
|
|
245
276
|
}
|
|
246
277
|
|
|
247
278
|
async function use(projectId) {
|
|
248
279
|
if (!projectId) { console.error("Usage: run402 projects use <project_id>"); process.exit(1); }
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
280
|
+
try {
|
|
281
|
+
await getSdk().projects.use(projectId);
|
|
282
|
+
console.log(JSON.stringify({ status: "ok", active_project_id: projectId }));
|
|
283
|
+
} catch (err) {
|
|
284
|
+
reportSdkError(err);
|
|
285
|
+
}
|
|
252
286
|
}
|
|
253
287
|
|
|
254
288
|
async function pin(projectId) {
|
|
255
289
|
if (!projectId) { console.error(JSON.stringify({ status: "error", message: "Usage: run402 projects pin <project_id>" })); process.exit(1); }
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
263
|
-
console.log(JSON.stringify(data, null, 2));
|
|
290
|
+
try {
|
|
291
|
+
const data = await getSdk().projects.pin(projectId);
|
|
292
|
+
console.log(JSON.stringify(data, null, 2));
|
|
293
|
+
} catch (err) {
|
|
294
|
+
reportSdkError(err);
|
|
295
|
+
}
|
|
264
296
|
}
|
|
265
297
|
|
|
266
298
|
async function promoteUser(projectId, email) {
|
|
@@ -290,14 +322,11 @@ async function demoteUser(projectId, email) {
|
|
|
290
322
|
}
|
|
291
323
|
|
|
292
324
|
async function deleteProject(projectId) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (res.status === 204 || res.ok) {
|
|
296
|
-
removeProject(projectId);
|
|
325
|
+
try {
|
|
326
|
+
await getSdk().projects.delete(projectId);
|
|
297
327
|
console.log(JSON.stringify({ status: "ok", message: `Project ${projectId} deleted.` }));
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1);
|
|
328
|
+
} catch (err) {
|
|
329
|
+
reportSdkError(err);
|
|
301
330
|
}
|
|
302
331
|
}
|
|
303
332
|
|
|
@@ -335,6 +364,8 @@ export async function run(sub, args) {
|
|
|
335
364
|
case "usage": { const { projectId } = resolvePositionalProject(args); await usage(projectId); break; }
|
|
336
365
|
case "schema": { const { projectId } = resolvePositionalProject(args); await schema(projectId); break; }
|
|
337
366
|
case "rls": { const { projectId, rest } = resolvePositionalProject(args); await rls(projectId, rest[0], rest[1]); break; }
|
|
367
|
+
case "apply-expose": { const { projectId, rest } = resolvePositionalProject(args); await applyExpose(projectId, rest); break; }
|
|
368
|
+
case "get-expose": { const { projectId } = resolvePositionalProject(args); await getExpose(projectId); break; }
|
|
338
369
|
case "delete": { const { projectId } = resolvePositionalProject(args); await deleteProject(projectId); break; }
|
|
339
370
|
case "pin": { const { projectId } = resolvePositionalProject(args); await pin(projectId); break; }
|
|
340
371
|
case "promote-user": { const { projectId, rest } = resolvePositionalProject(args); await promoteUser(projectId, rest[0]); break; }
|