run402 1.8.1 → 1.9.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 CHANGED
@@ -1,5 +1,4 @@
1
- import { readWallet, API, WALLET_FILE } from "./config.mjs";
2
- import { existsSync } from "fs";
1
+ import { API, walletAuthHeaders } from "./config.mjs";
3
2
 
4
3
  const HELP = `run402 agent — Manage agent identity
5
4
 
@@ -24,32 +23,15 @@ async function contact(args) {
24
23
  if (args[i] === "--webhook" && args[i + 1]) webhook = args[++i];
25
24
  }
26
25
  if (!name) { console.error(JSON.stringify({ status: "error", message: "Missing --name <name>" })); process.exit(1); }
27
- if (!existsSync(WALLET_FILE)) {
28
- console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" }));
29
- process.exit(1);
30
- }
31
-
32
- const wallet = readWallet();
33
- const { privateKeyToAccount } = await import("viem/accounts");
34
- const { createPublicClient, http } = await import("viem");
35
- const { baseSepolia } = await import("viem/chains");
36
- const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
37
- const { ExactEvmScheme } = await import("@x402/evm/exact/client");
38
- const { toClientEvmSigner } = await import("@x402/evm");
39
- const account = privateKeyToAccount(wallet.privateKey);
40
- const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
41
- const signer = toClientEvmSigner(account, publicClient);
42
- const client = new x402Client();
43
- client.register("eip155:84532", new ExactEvmScheme(signer));
44
- const fetchPaid = wrapFetchWithPayment(fetch, client);
26
+ const authHeaders = await walletAuthHeaders();
45
27
 
46
28
  const body = { name };
47
29
  if (email) body.email = email;
48
30
  if (webhook) body.webhook = webhook;
49
31
 
50
- const res = await fetchPaid(`${API}/agent/v1/contact`, {
32
+ const res = await fetch(`${API}/agent/v1/contact`, {
51
33
  method: "POST",
52
- headers: { "Content-Type": "application/json" },
34
+ headers: { "Content-Type": "application/json", ...authHeaders },
53
35
  body: JSON.stringify(body),
54
36
  });
55
37
  const data = await res.json();
package/lib/apps.mjs CHANGED
@@ -1,6 +1,4 @@
1
- import { existsSync } from "fs";
2
- import { findProject, readWallet, loadProjects, saveProjects, API, WALLET_FILE, PROJECTS_FILE } from "./config.mjs";
3
- import { mkdirSync, writeFileSync } from "fs";
1
+ import { findProject, API, walletAuthHeaders, saveProject } from "./config.mjs";
4
2
 
5
3
  const HELP = `run402 apps — Browse and manage the app marketplace
6
4
 
@@ -49,31 +47,14 @@ async function fork(versionId, name, args) {
49
47
  if (args[i] === "--tier" && args[i + 1]) opts.tier = args[++i];
50
48
  if (args[i] === "--subdomain" && args[i + 1]) opts.subdomain = args[++i];
51
49
  }
52
- if (!existsSync(WALLET_FILE)) {
53
- console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" }));
54
- process.exit(1);
55
- }
56
-
57
- const wallet = readWallet();
58
- const { privateKeyToAccount } = await import("viem/accounts");
59
- const { createPublicClient, http } = await import("viem");
60
- const { baseSepolia } = await import("viem/chains");
61
- const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
62
- const { ExactEvmScheme } = await import("@x402/evm/exact/client");
63
- const { toClientEvmSigner } = await import("@x402/evm");
64
- const account = privateKeyToAccount(wallet.privateKey);
65
- const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
66
- const signer = toClientEvmSigner(account, publicClient);
67
- const client = new x402Client();
68
- client.register("eip155:84532", new ExactEvmScheme(signer));
69
- const fetchPaid = wrapFetchWithPayment(fetch, client);
50
+ const authHeaders = await walletAuthHeaders();
70
51
 
71
52
  const body = { version_id: versionId, name };
72
53
  if (opts.subdomain) body.subdomain = opts.subdomain;
73
54
 
74
- const res = await fetchPaid(`${API}/fork/v1`, {
55
+ const res = await fetch(`${API}/fork/v1`, {
75
56
  method: "POST",
76
- headers: { "Content-Type": "application/json" },
57
+ headers: { "Content-Type": "application/json", ...authHeaders },
77
58
  body: JSON.stringify(body),
78
59
  });
79
60
  const data = await res.json();
@@ -81,15 +62,12 @@ async function fork(versionId, name, args) {
81
62
 
82
63
  // Save project credentials locally
83
64
  if (data.project_id) {
84
- const projects = loadProjects();
85
- projects.push({
86
- project_id: data.project_id, anon_key: data.anon_key, service_key: data.service_key,
65
+ saveProject(data.project_id, {
66
+ anon_key: data.anon_key, service_key: data.service_key,
87
67
  tier: data.tier, lease_expires_at: data.lease_expires_at,
88
- site_url: data.site_url || data.subdomain_url, deployed_at: new Date().toISOString(),
68
+ site_url: data.site_url || data.subdomain_url,
69
+ deployed_at: new Date().toISOString(),
89
70
  });
90
- const dir = PROJECTS_FILE.replace(/\/[^/]+$/, "");
91
- mkdirSync(dir, { recursive: true });
92
- writeFileSync(PROJECTS_FILE, JSON.stringify(projects, null, 2), { mode: 0o600 });
93
71
  }
94
72
  console.log(JSON.stringify(data, null, 2));
95
73
  }
package/lib/config.mjs CHANGED
@@ -1,43 +1,37 @@
1
1
  /**
2
- * Run402 config loader — reads local project and wallet state.
3
- * Kept in a separate module so credential reads stay isolated.
2
+ * Run402 config loader — thin wrapper over core/ shared modules.
3
+ * Adds CLI-specific behavior: process.exit() on errors.
4
4
  */
5
5
 
6
- import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, renameSync } from "fs";
7
- import { join, dirname } from "path";
8
- import { homedir } from "os";
9
- import { randomBytes } from "crypto";
6
+ import { getApiBase, getConfigDir, getKeystorePath, getWalletPath } from "../../core/dist/config.js";
7
+ import { readWallet as coreReadWallet, saveWallet as coreSaveWallet } from "../../core/dist/wallet.js";
8
+ import { getWalletAuthHeaders } from "../../core/dist/wallet-auth.js";
9
+ import { loadKeyStore, getProject, saveProject, removeProject, saveKeyStore } from "../../core/dist/keystore.js";
10
10
 
11
- export const CONFIG_DIR = join(homedir(), ".config", "run402");
12
- export const WALLET_FILE = join(CONFIG_DIR, "wallet.json");
13
- export const PROJECTS_FILE = join(CONFIG_DIR, "projects.json");
14
- export const API = "https://api.run402.com";
11
+ export const CONFIG_DIR = getConfigDir();
12
+ export const WALLET_FILE = getWalletPath();
13
+ export const PROJECTS_FILE = getKeystorePath();
14
+ export const API = getApiBase();
15
15
 
16
16
  export function readWallet() {
17
- if (!existsSync(WALLET_FILE)) return null;
18
- return JSON.parse(readFileSync(WALLET_FILE, "utf-8"));
17
+ return coreReadWallet();
19
18
  }
20
19
 
21
20
  export function saveWallet(data) {
22
- mkdirSync(CONFIG_DIR, { recursive: true });
23
- const tmp = join(CONFIG_DIR, `.wallet.${randomBytes(4).toString("hex")}.tmp`);
24
- writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 0o600 });
25
- renameSync(tmp, WALLET_FILE);
26
- chmodSync(WALLET_FILE, 0o600);
21
+ coreSaveWallet(data);
27
22
  }
28
23
 
29
- export function loadProjects() {
30
- if (!existsSync(PROJECTS_FILE)) return [];
31
- return JSON.parse(readFileSync(PROJECTS_FILE, "utf-8"));
32
- }
33
-
34
- export function saveProjects(projects) {
35
- mkdirSync(CONFIG_DIR, { recursive: true });
36
- writeFileSync(PROJECTS_FILE, JSON.stringify(projects, null, 2), { mode: 0o600 });
24
+ export async function walletAuthHeaders() {
25
+ const headers = getWalletAuthHeaders();
26
+ if (!headers) { console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create" })); process.exit(1); }
27
+ return headers;
37
28
  }
38
29
 
39
30
  export function findProject(id) {
40
- const p = loadProjects().find(p => p.project_id === id);
31
+ const p = getProject(id);
41
32
  if (!p) { console.error(`Project ${id} not found in local registry.`); process.exit(1); }
42
33
  return p;
43
34
  }
35
+
36
+ // Re-export core keystore functions for direct use
37
+ export { loadKeyStore, saveProject, removeProject, saveKeyStore };
package/lib/deploy.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
2
- import { readWallet, loadProjects, API, WALLET_FILE, PROJECTS_FILE } from "./config.mjs";
1
+ import { readFileSync } from "fs";
2
+ import { API, walletAuthHeaders, saveProject } from "./config.mjs";
3
3
 
4
4
  const HELP = `run402 deploy — Deploy a full-stack app or static site on Run402
5
5
 
@@ -39,14 +39,6 @@ async function readStdin() {
39
39
  return Buffer.concat(chunks).toString("utf-8");
40
40
  }
41
41
 
42
- function saveProject(project) {
43
- const projects = loadProjects();
44
- projects.push({ project_id: project.project_id, anon_key: project.anon_key, service_key: project.service_key, tier: project.tier, lease_expires_at: project.lease_expires_at, site_url: project.site_url || project.subdomain_url, deployed_at: new Date().toISOString() });
45
- const dir = PROJECTS_FILE.replace(/\/[^/]+$/, "");
46
- mkdirSync(dir, { recursive: true });
47
- writeFileSync(PROJECTS_FILE, JSON.stringify(projects, null, 2), { mode: 0o600 });
48
- }
49
-
50
42
  export async function run(args) {
51
43
  const opts = { manifest: null };
52
44
  for (let i = 0; i < args.length; i++) {
@@ -54,30 +46,19 @@ export async function run(args) {
54
46
  if (args[i] === "--manifest" && args[i + 1]) opts.manifest = args[++i];
55
47
  }
56
48
 
57
- if (!existsSync(WALLET_FILE)) {
58
- console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" }));
59
- process.exit(1);
60
- }
61
- const wallet = readWallet();
62
49
  const manifest = opts.manifest ? JSON.parse(readFileSync(opts.manifest, "utf-8")) : JSON.parse(await readStdin());
63
50
 
64
- const { privateKeyToAccount } = await import("viem/accounts");
65
- const { createPublicClient, http } = await import("viem");
66
- const { baseSepolia } = await import("viem/chains");
67
- const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
68
- const { ExactEvmScheme } = await import("@x402/evm/exact/client");
69
- const { toClientEvmSigner } = await import("@x402/evm");
70
-
71
- const account = privateKeyToAccount(wallet.privateKey);
72
- const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
73
- const signer = toClientEvmSigner(account, publicClient);
74
- const client = new x402Client();
75
- client.register("eip155:84532", new ExactEvmScheme(signer));
76
- const fetchPaid = wrapFetchWithPayment(fetch, client);
77
-
78
- const res = await fetchPaid(`${API}/deploy/v1`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(manifest) });
51
+ const authHeaders = await walletAuthHeaders();
52
+ const res = await fetch(`${API}/deploy/v1`, { method: "POST", headers: { "Content-Type": "application/json", ...authHeaders }, body: JSON.stringify(manifest) });
79
53
  const result = await res.json();
80
54
  if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...result })); process.exit(1); }
81
- saveProject(result);
55
+ if (result.project_id) {
56
+ saveProject(result.project_id, {
57
+ anon_key: result.anon_key, service_key: result.service_key,
58
+ tier: result.tier, lease_expires_at: result.lease_expires_at,
59
+ site_url: result.site_url || result.subdomain_url,
60
+ deployed_at: new Date().toISOString(),
61
+ });
62
+ }
82
63
  console.log(JSON.stringify(result, null, 2));
83
64
  }
package/lib/image.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { writeFileSync, existsSync } from "fs";
2
- import { readWallet, API, WALLET_FILE } from "./config.mjs";
1
+ import { writeFileSync } from "fs";
2
+ import { API, WALLET_FILE } from "./config.mjs";
3
+ import { setupPaidFetch } from "./paid-fetch.mjs";
3
4
 
4
5
  const HELP = `run402 image — Generate AI images via x402 micropayments
5
6
 
@@ -49,22 +50,8 @@ export async function run(sub, args) {
49
50
  }
50
51
 
51
52
  if (!opts.prompt) { console.error(JSON.stringify({ status: "error", message: "Prompt required. Usage: run402 image generate \"your prompt\"" })); process.exit(1); }
52
- if (!existsSync(WALLET_FILE)) { console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" })); process.exit(1); }
53
53
 
54
- const wallet = readWallet();
55
- const { privateKeyToAccount } = await import("viem/accounts");
56
- const { createPublicClient, http } = await import("viem");
57
- const { baseSepolia } = await import("viem/chains");
58
- const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
59
- const { ExactEvmScheme } = await import("@x402/evm/exact/client");
60
- const { toClientEvmSigner } = await import("@x402/evm");
61
-
62
- const account = privateKeyToAccount(wallet.privateKey);
63
- const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
64
- const signer = toClientEvmSigner(account, publicClient);
65
- const client = new x402Client();
66
- client.register("eip155:84532", new ExactEvmScheme(signer));
67
- const fetchPaid = wrapFetchWithPayment(fetch, client);
54
+ const fetchPaid = await setupPaidFetch();
68
55
 
69
56
  const res = await fetchPaid(`${API}/generate-image/v1`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: opts.prompt, aspect: opts.aspect }) });
70
57
  const data = await res.json();
package/lib/init.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { readWallet, saveWallet, loadProjects, CONFIG_DIR, WALLET_FILE, API } from "./config.mjs";
1
+ import { readWallet, saveWallet, loadKeyStore, CONFIG_DIR, WALLET_FILE, API } from "./config.mjs";
2
2
  import { mkdirSync } from "fs";
3
3
 
4
4
  const USDC_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }] }];
@@ -91,8 +91,8 @@ export async function run() {
91
91
  }
92
92
 
93
93
  // 5. Projects
94
- const projects = loadProjects();
95
- line("Projects", `${projects.length} active`);
94
+ const store = loadKeyStore();
95
+ line("Projects", `${Object.keys(store.projects).length} active`);
96
96
 
97
97
  // 6. Next step
98
98
  console.log();
package/lib/message.mjs CHANGED
@@ -1,5 +1,4 @@
1
- import { readWallet, API, WALLET_FILE } from "./config.mjs";
2
- import { existsSync } from "fs";
1
+ import { API, walletAuthHeaders } from "./config.mjs";
3
2
 
4
3
  const HELP = `run402 message — Send messages to Run402 developers
5
4
 
@@ -16,28 +15,11 @@ Examples:
16
15
 
17
16
  async function send(text) {
18
17
  if (!text) { console.error(JSON.stringify({ status: "error", message: "Missing message text" })); process.exit(1); }
19
- if (!existsSync(WALLET_FILE)) {
20
- console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" }));
21
- process.exit(1);
22
- }
23
-
24
- const wallet = readWallet();
25
- const { privateKeyToAccount } = await import("viem/accounts");
26
- const { createPublicClient, http } = await import("viem");
27
- const { baseSepolia } = await import("viem/chains");
28
- const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
29
- const { ExactEvmScheme } = await import("@x402/evm/exact/client");
30
- const { toClientEvmSigner } = await import("@x402/evm");
31
- const account = privateKeyToAccount(wallet.privateKey);
32
- const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
33
- const signer = toClientEvmSigner(account, publicClient);
34
- const client = new x402Client();
35
- client.register("eip155:84532", new ExactEvmScheme(signer));
36
- const fetchPaid = wrapFetchWithPayment(fetch, client);
18
+ const authHeaders = await walletAuthHeaders();
37
19
 
38
- const res = await fetchPaid(`${API}/message/v1`, {
20
+ const res = await fetch(`${API}/message/v1`, {
39
21
  method: "POST",
40
- headers: { "Content-Type": "application/json" },
22
+ headers: { "Content-Type": "application/json", ...authHeaders },
41
23
  body: JSON.stringify({ message: text }),
42
24
  });
43
25
  const data = await res.json();
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared x402 payment wrapper for CLI commands that need paid fetch.
3
+ * Uses viem for wallet signing + @x402/fetch for payment wrapping.
4
+ */
5
+
6
+ import { readWallet, WALLET_FILE } from "./config.mjs";
7
+ import { existsSync } from "fs";
8
+
9
+ export async function setupPaidFetch() {
10
+ if (!existsSync(WALLET_FILE)) {
11
+ console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" }));
12
+ process.exit(1);
13
+ }
14
+ const wallet = readWallet();
15
+ const { privateKeyToAccount } = await import("viem/accounts");
16
+ const { createPublicClient, http } = await import("viem");
17
+ const { baseSepolia } = await import("viem/chains");
18
+ const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
19
+ const { ExactEvmScheme } = await import("@x402/evm/exact/client");
20
+ const { toClientEvmSigner } = await import("@x402/evm");
21
+ const account = privateKeyToAccount(wallet.privateKey);
22
+ const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
23
+ const signer = toClientEvmSigner(account, publicClient);
24
+ const client = new x402Client();
25
+ client.register("eip155:84532", new ExactEvmScheme(signer));
26
+ return wrapFetchWithPayment(fetch, client);
27
+ }
package/lib/projects.mjs CHANGED
@@ -1,5 +1,4 @@
1
- import { findProject, loadProjects, saveProjects, readWallet, API, WALLET_FILE, PROJECTS_FILE } from "./config.mjs";
2
- import { existsSync, mkdirSync, writeFileSync } from "fs";
1
+ import { findProject, loadKeyStore, saveProject, removeProject, API, walletAuthHeaders } from "./config.mjs";
3
2
 
4
3
  const HELP = `run402 projects — Manage your deployed Run402 projects
5
4
 
@@ -36,26 +35,6 @@ Notes:
36
35
  - RLS templates: user_owns_rows, public_read, public_read_write
37
36
  `;
38
37
 
39
- async function setupPaidFetch() {
40
- if (!existsSync(WALLET_FILE)) {
41
- console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" }));
42
- process.exit(1);
43
- }
44
- const wallet = readWallet();
45
- const { privateKeyToAccount } = await import("viem/accounts");
46
- const { createPublicClient, http } = await import("viem");
47
- const { baseSepolia } = await import("viem/chains");
48
- const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
49
- const { ExactEvmScheme } = await import("@x402/evm/exact/client");
50
- const { toClientEvmSigner } = await import("@x402/evm");
51
- const account = privateKeyToAccount(wallet.privateKey);
52
- const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
53
- const signer = toClientEvmSigner(account, publicClient);
54
- const client = new x402Client();
55
- client.register("eip155:84532", new ExactEvmScheme(signer));
56
- return wrapFetchWithPayment(fetch, client);
57
- }
58
-
59
38
  async function quote() {
60
39
  const res = await fetch(`${API}/tiers/v1`);
61
40
  const data = await res.json();
@@ -69,26 +48,23 @@ async function provision(args) {
69
48
  if (args[i] === "--tier" && args[i + 1]) opts.tier = args[++i];
70
49
  if (args[i] === "--name" && args[i + 1]) opts.name = args[++i];
71
50
  }
72
- const fetchPaid = await setupPaidFetch();
51
+ const authHeaders = await walletAuthHeaders();
73
52
  const body = { tier: opts.tier };
74
53
  if (opts.name) body.name = opts.name;
75
- const res = await fetchPaid(`${API}/projects/v1`, {
54
+ const res = await fetch(`${API}/projects/v1`, {
76
55
  method: "POST",
77
- headers: { "Content-Type": "application/json" },
56
+ headers: { "Content-Type": "application/json", ...authHeaders },
78
57
  body: JSON.stringify(body),
79
58
  });
80
59
  const data = await res.json();
81
60
  if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
82
61
  // Save project credentials locally
83
62
  if (data.project_id) {
84
- const projects = loadProjects();
85
- projects.push({
86
- project_id: data.project_id, anon_key: data.anon_key, service_key: data.service_key,
87
- tier: data.tier, lease_expires_at: data.lease_expires_at, deployed_at: new Date().toISOString(),
63
+ saveProject(data.project_id, {
64
+ anon_key: data.anon_key, service_key: data.service_key,
65
+ tier: data.tier, lease_expires_at: data.lease_expires_at,
66
+ deployed_at: new Date().toISOString(),
88
67
  });
89
- const dir = PROJECTS_FILE.replace(/\/[^/]+$/, "");
90
- mkdirSync(dir, { recursive: true });
91
- writeFileSync(PROJECTS_FILE, JSON.stringify(projects, null, 2), { mode: 0o600 });
92
68
  }
93
69
  console.log(JSON.stringify(data, null, 2));
94
70
  }
@@ -107,9 +83,10 @@ async function rls(projectId, template, tablesJson) {
107
83
  }
108
84
 
109
85
  async function list() {
110
- const projects = loadProjects();
111
- if (projects.length === 0) { console.log(JSON.stringify({ status: "ok", projects: [], message: "No projects yet." })); return; }
112
- console.log(JSON.stringify(projects.map(p => ({ project_id: p.project_id, tier: p.tier, site_url: p.site_url, lease_expires_at: p.lease_expires_at, deployed_at: p.deployed_at })), null, 2));
86
+ const store = loadKeyStore();
87
+ const entries = Object.entries(store.projects);
88
+ if (entries.length === 0) { console.log(JSON.stringify({ status: "ok", projects: [], message: "No projects yet." })); return; }
89
+ console.log(JSON.stringify(entries.map(([id, p]) => ({ project_id: id, tier: p.tier, site_url: p.site_url, lease_expires_at: p.lease_expires_at, deployed_at: p.deployed_at })), null, 2));
113
90
  }
114
91
 
115
92
  async function sqlCmd(projectId, query) {
@@ -144,7 +121,7 @@ async function deleteProject(projectId) {
144
121
  const p = findProject(projectId);
145
122
  const res = await fetch(`${API}/projects/v1/${projectId}`, { method: "DELETE", headers: { "Authorization": `Bearer ${p.service_key}` } });
146
123
  if (res.status === 204 || res.ok) {
147
- saveProjects(loadProjects().filter(pr => pr.project_id !== projectId));
124
+ removeProject(projectId);
148
125
  console.log(JSON.stringify({ status: "ok", message: `Project ${projectId} deleted.` }));
149
126
  } else {
150
127
  const data = await res.json().catch(() => ({}));
package/lib/sites.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { readFileSync, existsSync } from "fs";
2
- import { readWallet, API, WALLET_FILE } from "./config.mjs";
1
+ import { readFileSync } from "fs";
2
+ import { API, walletAuthHeaders } from "./config.mjs";
3
3
 
4
4
  const HELP = `run402 sites — Deploy and manage static sites
5
5
 
@@ -53,33 +53,15 @@ async function deploy(args) {
53
53
  if (args[i] === "--target" && args[i + 1]) opts.target = args[++i];
54
54
  }
55
55
  if (!opts.name) { console.error(JSON.stringify({ status: "error", message: "Missing --name <name>" })); process.exit(1); }
56
- if (!existsSync(WALLET_FILE)) {
57
- console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" }));
58
- process.exit(1);
59
- }
60
-
61
56
  const manifest = opts.manifest ? JSON.parse(readFileSync(opts.manifest, "utf-8")) : JSON.parse(await readStdin());
62
57
  const body = { name: opts.name, files: manifest.files };
63
58
  if (opts.project) body.project = opts.project;
64
59
  if (opts.target) body.target = opts.target;
65
60
 
66
- const wallet = readWallet();
67
- const { privateKeyToAccount } = await import("viem/accounts");
68
- const { createPublicClient, http } = await import("viem");
69
- const { baseSepolia } = await import("viem/chains");
70
- const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
71
- const { ExactEvmScheme } = await import("@x402/evm/exact/client");
72
- const { toClientEvmSigner } = await import("@x402/evm");
73
- const account = privateKeyToAccount(wallet.privateKey);
74
- const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
75
- const signer = toClientEvmSigner(account, publicClient);
76
- const client = new x402Client();
77
- client.register("eip155:84532", new ExactEvmScheme(signer));
78
- const fetchPaid = wrapFetchWithPayment(fetch, client);
79
-
80
- const res = await fetchPaid(`${API}/deployments/v1`, {
61
+ const authHeaders = await walletAuthHeaders();
62
+ const res = await fetch(`${API}/deployments/v1`, {
81
63
  method: "POST",
82
- headers: { "Content-Type": "application/json" },
64
+ headers: { "Content-Type": "application/json", ...authHeaders },
83
65
  body: JSON.stringify(body),
84
66
  });
85
67
  const data = await res.json();
package/lib/tier.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { readWallet, WALLET_FILE, API } from "./config.mjs";
2
- import { existsSync } from "fs";
2
+ import { setupPaidFetch } from "./paid-fetch.mjs";
3
3
 
4
4
  const HELP = `run402 tier — Manage your Run402 tier subscription
5
5
 
@@ -24,26 +24,6 @@ Examples:
24
24
  run402 tier set hobby
25
25
  `;
26
26
 
27
- async function setupPaidFetch() {
28
- if (!existsSync(WALLET_FILE)) {
29
- console.error(JSON.stringify({ status: "error", message: "No wallet found. Run: run402 wallet create && run402 wallet fund" }));
30
- process.exit(1);
31
- }
32
- const wallet = readWallet();
33
- const { privateKeyToAccount } = await import("viem/accounts");
34
- const { createPublicClient, http } = await import("viem");
35
- const { baseSepolia } = await import("viem/chains");
36
- const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
37
- const { ExactEvmScheme } = await import("@x402/evm/exact/client");
38
- const { toClientEvmSigner } = await import("@x402/evm");
39
- const account = privateKeyToAccount(wallet.privateKey);
40
- const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
41
- const signer = toClientEvmSigner(account, publicClient);
42
- const client = new x402Client();
43
- client.register("eip155:84532", new ExactEvmScheme(signer));
44
- return wrapFetchWithPayment(fetch, client);
45
- }
46
-
47
27
  async function status() {
48
28
  const w = readWallet();
49
29
  if (!w) { console.log(JSON.stringify({ status: "error", message: "No wallet. Run: run402 wallet create" })); process.exit(1); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "description": "CLI for Run402 — provision Postgres databases, deploy static sites, generate images, and manage wallets via x402 micropayments.",
5
5
  "type": "module",
6
6
  "bin": {