@tothalex/nulljs 0.0.59 → 0.0.61
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/dist/cli.js +1519 -869
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -12,259 +12,37 @@ var __export = (target, all) => {
|
|
|
12
12
|
};
|
|
13
13
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
14
14
|
var __require = import.meta.require;
|
|
15
|
-
// ../../packages/api/types/index.ts
|
|
16
|
-
var init_types = () => {};
|
|
17
|
-
|
|
18
|
-
// ../../packages/api/actions/client.ts
|
|
19
|
-
var buildUrl = (path, options) => {
|
|
20
|
-
let fullPath = options?.url ? `${options.url}${path}` : path;
|
|
21
|
-
if (options?.query) {
|
|
22
|
-
const params = [];
|
|
23
|
-
for (const [key, value] of Object.entries(options.query)) {
|
|
24
|
-
if (value === undefined || value === null)
|
|
25
|
-
continue;
|
|
26
|
-
if (Array.isArray(value)) {
|
|
27
|
-
params.push(`${key}=${value.join(",")}`);
|
|
28
|
-
} else {
|
|
29
|
-
params.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const queryString = params.join("&");
|
|
33
|
-
if (queryString) {
|
|
34
|
-
fullPath = `${fullPath}?${queryString}`;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return fullPath;
|
|
38
|
-
}, apiGet = async (path, options) => {
|
|
39
|
-
const url = options?.url ? `${options.url}${path}` : path;
|
|
40
|
-
const headers = {};
|
|
41
|
-
if (options?.publicKey) {
|
|
42
|
-
headers["x-public-key"] = options.publicKey;
|
|
43
|
-
}
|
|
44
|
-
const response = await fetch(url, { headers });
|
|
45
|
-
if (!response.ok) {
|
|
46
|
-
const message = await response.text().catch(() => "");
|
|
47
|
-
throw new Error(`HTTP error! status: ${response.status}${message ? ` message: ${message}` : ""}`);
|
|
48
|
-
}
|
|
49
|
-
return response.json();
|
|
50
|
-
};
|
|
51
15
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
var getMimeType = (fileName) => {
|
|
61
|
-
if (fileName.endsWith(".js"))
|
|
62
|
-
return "application/javascript";
|
|
63
|
-
if (fileName.endsWith(".css"))
|
|
64
|
-
return "text/css";
|
|
65
|
-
if (fileName.endsWith(".html"))
|
|
66
|
-
return "text/html";
|
|
67
|
-
if (fileName.endsWith(".json"))
|
|
68
|
-
return "application/json";
|
|
69
|
-
return "application/octet-stream";
|
|
70
|
-
}, createFormDataWithInfo = (assets2) => {
|
|
71
|
-
const form = new FormData;
|
|
72
|
-
const blobs = [];
|
|
73
|
-
for (const asset of assets2) {
|
|
74
|
-
const mime = getMimeType(asset.fileName);
|
|
75
|
-
const buffer = Buffer.from(asset.code);
|
|
76
|
-
const blob = new Blob([buffer], { type: mime });
|
|
77
|
-
form.append(asset.fileName, blob);
|
|
78
|
-
blobs.push({ fileName: asset.fileName, size: blob.size, type: mime });
|
|
79
|
-
}
|
|
80
|
-
return { form, blobs };
|
|
81
|
-
}, createSignature = async (blobs, privateKey) => {
|
|
82
|
-
const sortedBlobs = [...blobs].sort((a, b) => a.fileName.localeCompare(b.fileName));
|
|
83
|
-
const dataString = sortedBlobs.map((b) => `${b.fileName}:${b.size}:${b.type}`).join("|");
|
|
84
|
-
const sign = await crypto.subtle.sign("Ed25519", privateKey, Buffer.from(dataString));
|
|
85
|
-
return btoa(String.fromCharCode(...new Uint8Array(sign)));
|
|
86
|
-
}, postWithCurl = async (url, signature, assets2) => {
|
|
87
|
-
const tempDir = mkdtempSync(join(tmpdir(), "nulljs-deploy-"));
|
|
88
|
-
try {
|
|
89
|
-
const formParts = [];
|
|
90
|
-
for (const asset of assets2) {
|
|
91
|
-
const filePath = join(tempDir, asset.fileName);
|
|
92
|
-
writeFileSync(filePath, asset.code);
|
|
93
|
-
const mime = getMimeType(asset.fileName);
|
|
94
|
-
formParts.push(`-F`);
|
|
95
|
-
formParts.push(`${asset.fileName}=@${filePath};type=${mime}`);
|
|
96
|
-
}
|
|
97
|
-
const result = await $`curl -s -w '\n%{http_code}' -X POST -H "authorization: ${signature}" ${formParts} ${url}`.text();
|
|
98
|
-
const lines = result.trim().split(`
|
|
99
|
-
`);
|
|
100
|
-
const statusCode = parseInt(lines.pop() || "0", 10);
|
|
101
|
-
const body = lines.join(`
|
|
102
|
-
`);
|
|
103
|
-
return { ok: statusCode >= 200 && statusCode < 300, status: statusCode, text: body };
|
|
104
|
-
} finally {
|
|
105
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
106
|
-
}
|
|
107
|
-
}, createFunctionDeployment = async (deployment2, options) => {
|
|
108
|
-
const handler = deployment2.assets.find((asset) => asset.fileName === "handler.js");
|
|
109
|
-
if (!handler) {
|
|
110
|
-
throw new Error(`Handler not found for ${deployment2.name}`);
|
|
111
|
-
}
|
|
112
|
-
const { blobs } = createFormDataWithInfo([handler]);
|
|
113
|
-
const signature = await createSignature(blobs, options.privateKey);
|
|
114
|
-
const baseUrl = options.url || "";
|
|
115
|
-
const url = `${baseUrl}/api/deployment`;
|
|
116
|
-
if (options.useCurl) {
|
|
117
|
-
const result = await postWithCurl(url, signature, [handler]);
|
|
118
|
-
if (!result.ok) {
|
|
119
|
-
throw new Error(`Deployment failed (${result.status}): ${result.text}`);
|
|
120
|
-
}
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const { form } = createFormDataWithInfo([handler]);
|
|
124
|
-
const response = await fetch(url, {
|
|
125
|
-
method: "POST",
|
|
126
|
-
headers: {
|
|
127
|
-
authorization: signature
|
|
128
|
-
},
|
|
129
|
-
body: form
|
|
130
|
-
});
|
|
131
|
-
if (!response.ok) {
|
|
132
|
-
const errorText = await response.text();
|
|
133
|
-
throw new Error(`Deployment failed (${response.status}): ${errorText}`);
|
|
134
|
-
}
|
|
135
|
-
}, createReactDeployment = async (deployment2, options) => {
|
|
136
|
-
const { blobs } = createFormDataWithInfo(deployment2.assets);
|
|
137
|
-
const signature = await createSignature(blobs, options.privateKey);
|
|
138
|
-
const baseUrl = options.url || "";
|
|
139
|
-
const url = `${baseUrl}/api/deployment/react`;
|
|
140
|
-
if (options.useCurl) {
|
|
141
|
-
const result = await postWithCurl(url, signature, deployment2.assets);
|
|
142
|
-
if (!result.ok) {
|
|
143
|
-
throw new Error(`Deployment failed (${result.status}): ${result.text}`);
|
|
16
|
+
// src/lib/paths.ts
|
|
17
|
+
import { existsSync } from "fs";
|
|
18
|
+
import { join, dirname } from "path";
|
|
19
|
+
var findProjectRoot = (startPath = process.cwd()) => {
|
|
20
|
+
let currentPath = startPath;
|
|
21
|
+
while (currentPath !== dirname(currentPath)) {
|
|
22
|
+
if (existsSync(join(currentPath, "package.json"))) {
|
|
23
|
+
return currentPath;
|
|
144
24
|
}
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
const { form } = createFormDataWithInfo(deployment2.assets);
|
|
148
|
-
const response = await fetch(url, {
|
|
149
|
-
method: "POST",
|
|
150
|
-
headers: {
|
|
151
|
-
authorization: signature
|
|
152
|
-
},
|
|
153
|
-
body: form
|
|
154
|
-
});
|
|
155
|
-
if (!response.ok) {
|
|
156
|
-
const errorText = await response.text();
|
|
157
|
-
throw new Error(`Deployment failed (${response.status}): ${errorText}`);
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
var init_deploy = () => {};
|
|
161
|
-
|
|
162
|
-
// ../../packages/api/actions/invocations.ts
|
|
163
|
-
var init_invocations = () => {};
|
|
164
|
-
|
|
165
|
-
// ../../packages/api/actions/logs.ts
|
|
166
|
-
var getSystemLogs = async (public_key, query, url) => {
|
|
167
|
-
const path = buildUrl("/api/system/logs", { url, query });
|
|
168
|
-
return apiGet(path, { publicKey: public_key });
|
|
169
|
-
}, searchDeploymentLogs = async (public_key, props, url) => {
|
|
170
|
-
const path = buildUrl("/api/logs/search", { url, query: props });
|
|
171
|
-
return apiGet(path, { publicKey: public_key });
|
|
172
|
-
};
|
|
173
|
-
var init_logs = () => {};
|
|
174
|
-
|
|
175
|
-
// ../../packages/api/actions/assets.ts
|
|
176
|
-
var init_assets = () => {};
|
|
177
|
-
|
|
178
|
-
// ../../packages/api/actions/activity.ts
|
|
179
|
-
var init_activity = () => {};
|
|
180
|
-
|
|
181
|
-
// ../../packages/api/actions/secrets.ts
|
|
182
|
-
var createSignatureHeader = async (privateKey, props) => {
|
|
183
|
-
const timestamp = new Date().toISOString();
|
|
184
|
-
const raw = `${props.method}-${props.path}-${timestamp}${props.body ? "-" + props.body : ""}`;
|
|
185
|
-
const sign = await crypto.subtle.sign("Ed25519", privateKey, Buffer.from(raw));
|
|
186
|
-
const signature = btoa(String.fromCharCode(...new Uint8Array(sign)));
|
|
187
|
-
const header = {
|
|
188
|
-
authorization: signature,
|
|
189
|
-
"x-time": timestamp
|
|
190
|
-
};
|
|
191
|
-
if (props.body) {
|
|
192
|
-
header["x-body"] = btoa(props.body);
|
|
193
|
-
}
|
|
194
|
-
return header;
|
|
195
|
-
}, listSecrets = async (options) => {
|
|
196
|
-
const baseUrl = options.url || "";
|
|
197
|
-
const path = `${baseUrl}/api/secrets`;
|
|
198
|
-
const headers = await createSignatureHeader(options.privateKey, {
|
|
199
|
-
method: "GET",
|
|
200
|
-
path
|
|
201
|
-
});
|
|
202
|
-
let response;
|
|
203
|
-
try {
|
|
204
|
-
response = await fetch(path, {
|
|
205
|
-
method: "GET",
|
|
206
|
-
headers
|
|
207
|
-
});
|
|
208
|
-
} catch (err) {
|
|
209
|
-
throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
|
|
210
|
-
}
|
|
211
|
-
if (!response.ok) {
|
|
212
|
-
const errorText = await response.text();
|
|
213
|
-
throw new Error(`Failed to list secrets (${response.status}): ${errorText}`);
|
|
214
|
-
}
|
|
215
|
-
return response.json();
|
|
216
|
-
}, createSecret = async (secret, options) => {
|
|
217
|
-
const baseUrl = options.url || "";
|
|
218
|
-
const path = `${baseUrl}/api/secrets`;
|
|
219
|
-
const body = JSON.stringify(secret);
|
|
220
|
-
const headers = await createSignatureHeader(options.privateKey, {
|
|
221
|
-
method: "POST",
|
|
222
|
-
path,
|
|
223
|
-
body
|
|
224
|
-
});
|
|
225
|
-
const response = await fetch(path, {
|
|
226
|
-
method: "POST",
|
|
227
|
-
headers: {
|
|
228
|
-
"Content-Type": "application/json",
|
|
229
|
-
...headers
|
|
230
|
-
},
|
|
231
|
-
body
|
|
232
|
-
});
|
|
233
|
-
if (!response.ok) {
|
|
234
|
-
const errorText = await response.text();
|
|
235
|
-
throw new Error(`Failed to create secret (${response.status}): ${errorText}`);
|
|
25
|
+
currentPath = dirname(currentPath);
|
|
236
26
|
}
|
|
27
|
+
return startPath;
|
|
28
|
+
}, getCloudPath = () => {
|
|
29
|
+
return join(findProjectRoot(), ".nulljs");
|
|
237
30
|
};
|
|
238
|
-
|
|
239
|
-
// ../../packages/api/actions/index.ts
|
|
240
|
-
var init_actions = __esm(() => {
|
|
241
|
-
init_deployments();
|
|
242
|
-
init_deploy();
|
|
243
|
-
init_invocations();
|
|
244
|
-
init_logs();
|
|
245
|
-
init_assets();
|
|
246
|
-
init_activity();
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// ../../packages/api/index.ts
|
|
250
|
-
var init_api = __esm(() => {
|
|
251
|
-
init_types();
|
|
252
|
-
init_actions();
|
|
253
|
-
});
|
|
31
|
+
var init_paths = () => {};
|
|
254
32
|
|
|
255
33
|
// src/config/index.ts
|
|
256
|
-
import { readFileSync, writeFileSync as
|
|
34
|
+
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync, readdirSync, rmSync } from "fs";
|
|
257
35
|
import { join as join2 } from "path";
|
|
258
36
|
import chalk from "chalk";
|
|
259
|
-
var getLocalConfigDir = () =>
|
|
37
|
+
var getLocalConfigDir = () => getCloudPath(), getLocalConfigFile = () => join2(getLocalConfigDir(), "config.json"), ensureLocalConfigDir = () => {
|
|
260
38
|
const localDir = getLocalConfigDir();
|
|
261
|
-
if (!
|
|
39
|
+
if (!existsSync2(localDir)) {
|
|
262
40
|
mkdirSync(localDir, { recursive: true });
|
|
263
41
|
}
|
|
264
42
|
}, readConfigStore = () => {
|
|
265
43
|
try {
|
|
266
44
|
const localFile = getLocalConfigFile();
|
|
267
|
-
if (!
|
|
45
|
+
if (!existsSync2(localFile)) {
|
|
268
46
|
return null;
|
|
269
47
|
}
|
|
270
48
|
const data = readFileSync(localFile, "utf-8");
|
|
@@ -274,7 +52,7 @@ var getLocalConfigDir = () => join2(process.cwd(), ".nulljs"), getLocalConfigFil
|
|
|
274
52
|
}
|
|
275
53
|
}, writeConfigStore = (store) => {
|
|
276
54
|
ensureLocalConfigDir();
|
|
277
|
-
|
|
55
|
+
writeFileSync(getLocalConfigFile(), JSON.stringify(store, null, 2));
|
|
278
56
|
}, readLocalConfig = () => {
|
|
279
57
|
const store = readConfigStore();
|
|
280
58
|
if (!store)
|
|
@@ -368,46 +146,59 @@ var getLocalConfigDir = () => join2(process.cwd(), ".nulljs"), getLocalConfigFil
|
|
|
368
146
|
}, loadPrivateKey = async (config) => {
|
|
369
147
|
const privateKeyBytes = Uint8Array.from(atob(config.key.private), (c) => c.charCodeAt(0));
|
|
370
148
|
return crypto.subtle.importKey("pkcs8", privateKeyBytes, { name: "Ed25519", namedCurve: "Ed25519" }, false, ["sign"]);
|
|
149
|
+
}, cleanLocalData = () => {
|
|
150
|
+
const localDir = getLocalConfigDir();
|
|
151
|
+
const deleted = [];
|
|
152
|
+
const preserved = [];
|
|
153
|
+
if (!existsSync2(localDir)) {
|
|
154
|
+
return { deleted, preserved };
|
|
155
|
+
}
|
|
156
|
+
const entries = readdirSync(localDir);
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
if (entry === "config.json") {
|
|
159
|
+
preserved.push(entry);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const entryPath = join2(localDir, entry);
|
|
163
|
+
try {
|
|
164
|
+
rmSync(entryPath, { recursive: true, force: true });
|
|
165
|
+
deleted.push(entry);
|
|
166
|
+
} catch {}
|
|
167
|
+
}
|
|
168
|
+
return { deleted, preserved };
|
|
371
169
|
};
|
|
372
|
-
var init_config = () => {
|
|
170
|
+
var init_config = __esm(() => {
|
|
171
|
+
init_paths();
|
|
172
|
+
});
|
|
373
173
|
|
|
374
174
|
// src/lib/server.ts
|
|
375
|
-
import { existsSync as
|
|
376
|
-
import { join as join3, dirname } from "path";
|
|
175
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, realpathSync } from "fs";
|
|
176
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
377
177
|
import chalk2 from "chalk";
|
|
378
178
|
var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
379
179
|
return `${process.platform}-${process.arch}`;
|
|
380
|
-
}, findProjectRoot = (startPath = process.cwd()) => {
|
|
381
|
-
let currentPath = startPath;
|
|
382
|
-
while (currentPath !== dirname(currentPath)) {
|
|
383
|
-
if (existsSync2(join3(currentPath, "package.json"))) {
|
|
384
|
-
return currentPath;
|
|
385
|
-
}
|
|
386
|
-
currentPath = dirname(currentPath);
|
|
387
|
-
}
|
|
388
|
-
return startPath;
|
|
389
180
|
}, findMonorepoRoot = (startPath = process.cwd()) => {
|
|
390
181
|
let currentPath = startPath;
|
|
391
|
-
while (currentPath !==
|
|
392
|
-
if (
|
|
182
|
+
while (currentPath !== dirname2(currentPath)) {
|
|
183
|
+
if (existsSync3(join3(currentPath, "Cargo.toml")) && existsSync3(join3(currentPath, "package.json"))) {
|
|
393
184
|
return currentPath;
|
|
394
185
|
}
|
|
395
|
-
currentPath =
|
|
186
|
+
currentPath = dirname2(currentPath);
|
|
396
187
|
}
|
|
397
188
|
return null;
|
|
398
189
|
}, getLocalBinaryPath = () => {
|
|
399
190
|
const envBinary = process.env.NULLJS_SERVER_BINARY;
|
|
400
|
-
if (envBinary &&
|
|
191
|
+
if (envBinary && existsSync3(envBinary)) {
|
|
401
192
|
return { path: envBinary, source: "local-env" };
|
|
402
193
|
}
|
|
403
194
|
const monorepoRoot = findMonorepoRoot() || findMonorepoRoot(CLI_DIR);
|
|
404
195
|
if (monorepoRoot) {
|
|
405
196
|
const debugBinary = join3(monorepoRoot, "target", "debug", "server");
|
|
406
|
-
if (
|
|
197
|
+
if (existsSync3(debugBinary)) {
|
|
407
198
|
return { path: debugBinary, source: "local-debug" };
|
|
408
199
|
}
|
|
409
200
|
const releaseBinary = join3(monorepoRoot, "target", "release", "server");
|
|
410
|
-
if (
|
|
201
|
+
if (existsSync3(releaseBinary)) {
|
|
411
202
|
return { path: releaseBinary, source: "local-release" };
|
|
412
203
|
}
|
|
413
204
|
}
|
|
@@ -428,10 +219,9 @@ var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
|
428
219
|
const args = ["--dev"];
|
|
429
220
|
args.push("--api-port", "3000");
|
|
430
221
|
args.push("--gateway-port", "3001");
|
|
431
|
-
const
|
|
432
|
-
const cloudPath = join3(projectRoot, ".nulljs");
|
|
222
|
+
const cloudPath = getCloudPath();
|
|
433
223
|
args.push("--cloud-path", cloudPath);
|
|
434
|
-
if (!
|
|
224
|
+
if (!existsSync3(cloudPath)) {
|
|
435
225
|
mkdirSync2(cloudPath, { recursive: true });
|
|
436
226
|
}
|
|
437
227
|
args.push("--public-key", devConfig.key.public);
|
|
@@ -473,7 +263,8 @@ var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
|
473
263
|
};
|
|
474
264
|
var init_server = __esm(() => {
|
|
475
265
|
init_config();
|
|
476
|
-
|
|
266
|
+
init_paths();
|
|
267
|
+
CLI_DIR = dirname2(realpathSync(import.meta.dir));
|
|
477
268
|
PLATFORMS = {
|
|
478
269
|
"linux-x64": "@tothalex/nulljs-linux-x64",
|
|
479
270
|
"linux-arm64": "@tothalex/nulljs-linux-arm64",
|
|
@@ -680,77 +471,254 @@ var init_bundle = __esm(() => {
|
|
|
680
471
|
init_function();
|
|
681
472
|
});
|
|
682
473
|
|
|
683
|
-
//
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
474
|
+
// ../../packages/api/actions/http.ts
|
|
475
|
+
import { mkdtempSync, writeFileSync as writeFileSync2, rmSync as rmSync2 } from "fs";
|
|
476
|
+
import { tmpdir } from "os";
|
|
477
|
+
import { join as join4 } from "path";
|
|
478
|
+
var {$ } = globalThis.Bun;
|
|
479
|
+
var parseCurlResponse = (result) => {
|
|
480
|
+
const lines = result.trim().split(`
|
|
481
|
+
`);
|
|
482
|
+
const status = parseInt(lines.pop() || "0", 10);
|
|
483
|
+
const body = lines.join(`
|
|
484
|
+
`);
|
|
485
|
+
return {
|
|
486
|
+
ok: status >= 200 && status < 300,
|
|
487
|
+
status,
|
|
488
|
+
body
|
|
489
|
+
};
|
|
490
|
+
}, buildHeaderFlags = (headers) => {
|
|
491
|
+
const flags = [];
|
|
492
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
493
|
+
flags.push("-H");
|
|
494
|
+
flags.push(`${key}: ${value}`);
|
|
495
|
+
}
|
|
496
|
+
return flags;
|
|
497
|
+
}, httpGet = async (path, options) => {
|
|
498
|
+
const url = options?.baseUrl ? `${options.baseUrl}${path}` : path;
|
|
499
|
+
const headers = options?.headers || {};
|
|
500
|
+
if (options?.useCurl) {
|
|
501
|
+
const headerFlags = buildHeaderFlags(headers);
|
|
502
|
+
const result = await $`curl -s -w '\n%{http_code}' -X GET ${headerFlags} ${url}`.text();
|
|
503
|
+
return parseCurlResponse(result);
|
|
702
504
|
}
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
505
|
+
const response = await fetch(url, {
|
|
506
|
+
method: "GET",
|
|
507
|
+
headers
|
|
508
|
+
});
|
|
509
|
+
return {
|
|
510
|
+
ok: response.ok,
|
|
511
|
+
status: response.status,
|
|
512
|
+
body: await response.text()
|
|
513
|
+
};
|
|
514
|
+
}, httpPost = async (path, body, options) => {
|
|
515
|
+
const url = options?.baseUrl ? `${options.baseUrl}${path}` : path;
|
|
516
|
+
const headers = options?.headers || {};
|
|
517
|
+
if (options?.useCurl) {
|
|
518
|
+
const tempDir = mkdtempSync(join4(tmpdir(), "nulljs-http-"));
|
|
519
|
+
const bodyFile = join4(tempDir, "body.json");
|
|
520
|
+
try {
|
|
521
|
+
writeFileSync2(bodyFile, body);
|
|
522
|
+
const headerFlags = buildHeaderFlags(headers);
|
|
523
|
+
const result = await $`curl -s -w '\n%{http_code}' -X POST ${headerFlags} -d @${bodyFile} ${url}`.text();
|
|
524
|
+
return parseCurlResponse(result);
|
|
525
|
+
} finally {
|
|
526
|
+
rmSync2(tempDir, { recursive: true, force: true });
|
|
527
|
+
}
|
|
707
528
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
529
|
+
const response = await fetch(url, {
|
|
530
|
+
method: "POST",
|
|
531
|
+
headers,
|
|
532
|
+
body
|
|
533
|
+
});
|
|
534
|
+
return {
|
|
535
|
+
ok: response.ok,
|
|
536
|
+
status: response.status,
|
|
537
|
+
body: await response.text()
|
|
538
|
+
};
|
|
539
|
+
}, httpPostFormData = async (path, files, options) => {
|
|
540
|
+
const url = options?.baseUrl ? `${options.baseUrl}${path}` : path;
|
|
541
|
+
const headers = options?.headers || {};
|
|
542
|
+
if (options?.useCurl) {
|
|
543
|
+
const tempDir = mkdtempSync(join4(tmpdir(), "nulljs-http-"));
|
|
544
|
+
try {
|
|
545
|
+
const formParts = [];
|
|
546
|
+
for (const file of files) {
|
|
547
|
+
const filePath = join4(tempDir, file.fileName);
|
|
548
|
+
writeFileSync2(filePath, file.content);
|
|
549
|
+
formParts.push("-F");
|
|
550
|
+
formParts.push(`${file.fileName}=@${filePath};type=${file.mimeType}`);
|
|
551
|
+
}
|
|
552
|
+
const headerFlags = buildHeaderFlags(headers);
|
|
553
|
+
const result = await $`curl -s -w '\n%{http_code}' -X POST ${headerFlags} ${formParts} ${url}`.text();
|
|
554
|
+
return parseCurlResponse(result);
|
|
555
|
+
} finally {
|
|
556
|
+
rmSync2(tempDir, { recursive: true, force: true });
|
|
720
557
|
}
|
|
721
558
|
}
|
|
722
|
-
const
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
559
|
+
const form = new FormData;
|
|
560
|
+
for (const file of files) {
|
|
561
|
+
const buffer = Buffer.from(file.content);
|
|
562
|
+
const blob = new Blob([buffer], { type: file.mimeType });
|
|
563
|
+
form.append(file.fileName, blob);
|
|
727
564
|
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
565
|
+
const response = await fetch(url, {
|
|
566
|
+
method: "POST",
|
|
567
|
+
headers,
|
|
568
|
+
body: form
|
|
569
|
+
});
|
|
570
|
+
return {
|
|
571
|
+
ok: response.ok,
|
|
572
|
+
status: response.status,
|
|
573
|
+
body: await response.text()
|
|
574
|
+
};
|
|
575
|
+
};
|
|
576
|
+
var init_http = () => {};
|
|
577
|
+
|
|
578
|
+
// ../../packages/api/actions/deploy.ts
|
|
579
|
+
var getMimeType = (fileName) => {
|
|
580
|
+
if (fileName.endsWith(".js"))
|
|
581
|
+
return "application/javascript";
|
|
582
|
+
if (fileName.endsWith(".css"))
|
|
583
|
+
return "text/css";
|
|
584
|
+
if (fileName.endsWith(".html"))
|
|
585
|
+
return "text/html";
|
|
586
|
+
if (fileName.endsWith(".json"))
|
|
587
|
+
return "application/json";
|
|
588
|
+
return "application/octet-stream";
|
|
589
|
+
}, assetsToFileData = (assets) => {
|
|
590
|
+
const files = [];
|
|
591
|
+
const blobs = [];
|
|
592
|
+
for (const asset of assets) {
|
|
593
|
+
const mimeType = getMimeType(asset.fileName);
|
|
594
|
+
const buffer = Buffer.from(asset.code);
|
|
595
|
+
files.push({
|
|
596
|
+
fileName: asset.fileName,
|
|
597
|
+
content: asset.code,
|
|
598
|
+
mimeType
|
|
599
|
+
});
|
|
600
|
+
blobs.push({
|
|
601
|
+
fileName: asset.fileName,
|
|
602
|
+
size: buffer.length,
|
|
603
|
+
type: mimeType
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return { files, blobs };
|
|
607
|
+
}, createSignature = async (blobs, privateKey) => {
|
|
608
|
+
const sortedBlobs = [...blobs].sort((a, b) => a.fileName.localeCompare(b.fileName));
|
|
609
|
+
const dataString = sortedBlobs.map((b) => `${b.fileName}:${b.size}:${b.type}`).join("|");
|
|
610
|
+
const sign = await crypto.subtle.sign("Ed25519", privateKey, Buffer.from(dataString));
|
|
611
|
+
return btoa(String.fromCharCode(...new Uint8Array(sign)));
|
|
612
|
+
}, createFunctionDeployment = async (deployment, options) => {
|
|
613
|
+
const handler = deployment.assets.find((asset) => asset.fileName === "handler.js");
|
|
614
|
+
if (!handler) {
|
|
615
|
+
throw new Error(`Handler not found for ${deployment.name}`);
|
|
616
|
+
}
|
|
617
|
+
const { files, blobs } = assetsToFileData([handler]);
|
|
618
|
+
const signature = await createSignature(blobs, options.privateKey);
|
|
619
|
+
const baseUrl = options.url || "";
|
|
620
|
+
const path = "/api/deployment";
|
|
621
|
+
const result = await httpPostFormData(path, files, {
|
|
622
|
+
baseUrl,
|
|
623
|
+
useCurl: options.useCurl,
|
|
624
|
+
headers: {
|
|
625
|
+
authorization: signature
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
if (!result.ok) {
|
|
629
|
+
throw new Error(`Deployment failed (${result.status}): ${result.body}`);
|
|
630
|
+
}
|
|
631
|
+
}, createReactDeployment = async (deployment, options) => {
|
|
632
|
+
const { files, blobs } = assetsToFileData(deployment.assets);
|
|
633
|
+
const signature = await createSignature(blobs, options.privateKey);
|
|
634
|
+
const baseUrl = options.url || "";
|
|
635
|
+
const path = "/api/deployment/react";
|
|
636
|
+
const result = await httpPostFormData(path, files, {
|
|
637
|
+
baseUrl,
|
|
638
|
+
useCurl: options.useCurl,
|
|
639
|
+
headers: {
|
|
640
|
+
authorization: signature
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
if (!result.ok) {
|
|
644
|
+
throw new Error(`Deployment failed (${result.status}): ${result.body}`);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
var init_deploy = __esm(() => {
|
|
648
|
+
init_http();
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// src/lib/deploy.ts
|
|
652
|
+
var exports_deploy = {};
|
|
653
|
+
__export(exports_deploy, {
|
|
654
|
+
deployReact: () => deployReact,
|
|
655
|
+
deployFunction: () => deployFunction,
|
|
656
|
+
clearDeployCache: () => clearDeployCache
|
|
657
|
+
});
|
|
658
|
+
import { basename as basename2 } from "path";
|
|
659
|
+
import { build } from "vite";
|
|
660
|
+
var deployedHashes, clearDeployCache = () => {
|
|
661
|
+
deployedHashes.clear();
|
|
662
|
+
}, hashCode = (code) => {
|
|
663
|
+
return Bun.hash(code).toString(16);
|
|
664
|
+
}, deployFunction = async (filePath, privateKey, apiUrl, useCurl) => {
|
|
665
|
+
const fileName = basename2(filePath);
|
|
666
|
+
const result = await build(functionConfig(filePath));
|
|
667
|
+
const handler = result.output.find((output) => output.type === "chunk" && output.fileName === "handler.js");
|
|
668
|
+
if (!handler) {
|
|
669
|
+
throw new Error("Bundle failed: handler.js not found in output");
|
|
670
|
+
}
|
|
671
|
+
const hash = hashCode(handler.code);
|
|
672
|
+
const lastHash = deployedHashes.get(filePath);
|
|
673
|
+
if (lastHash === hash) {
|
|
674
|
+
return { deployed: false, skipped: true };
|
|
675
|
+
}
|
|
676
|
+
await createFunctionDeployment({ name: fileName, assets: [{ fileName: "handler.js", code: handler.code }] }, { privateKey, url: apiUrl, useCurl });
|
|
677
|
+
deployedHashes.set(filePath, hash);
|
|
678
|
+
return { deployed: true, skipped: false };
|
|
679
|
+
}, deployReact = async (filePath, privateKey, apiUrl, useCurl) => {
|
|
680
|
+
const fileName = basename2(filePath);
|
|
681
|
+
const result = await build(spaClientConfig(filePath));
|
|
682
|
+
const assets = [];
|
|
683
|
+
for (const output of result.output) {
|
|
684
|
+
if (output.type === "chunk") {
|
|
685
|
+
assets.push({ fileName: output.fileName, code: output.code });
|
|
686
|
+
} else if (output.type === "asset" && typeof output.source === "string") {
|
|
687
|
+
assets.push({ fileName: output.fileName, code: output.source });
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
const combinedCode = assets.map((a) => a.code).join("");
|
|
691
|
+
const hash = hashCode(combinedCode);
|
|
692
|
+
const lastHash = deployedHashes.get(filePath);
|
|
693
|
+
if (lastHash === hash) {
|
|
694
|
+
return { deployed: false, skipped: true };
|
|
695
|
+
}
|
|
696
|
+
await createReactDeployment({ name: fileName, assets }, { privateKey, url: apiUrl, useCurl });
|
|
697
|
+
deployedHashes.set(filePath, hash);
|
|
698
|
+
return { deployed: true, skipped: false };
|
|
699
|
+
};
|
|
700
|
+
var init_deploy2 = __esm(() => {
|
|
701
|
+
init_deploy();
|
|
734
702
|
init_bundle();
|
|
735
703
|
deployedHashes = new Map;
|
|
736
704
|
});
|
|
737
705
|
|
|
738
706
|
// src/lib/watcher.ts
|
|
739
|
-
import { watch, existsSync as
|
|
707
|
+
import { watch, existsSync as existsSync4 } from "fs";
|
|
740
708
|
import { readdir } from "fs/promises";
|
|
741
|
-
import { join as
|
|
709
|
+
import { join as join5 } from "path";
|
|
742
710
|
import { build as build2 } from "vite";
|
|
743
711
|
var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
744
712
|
return Bun.hash(code).toString(16);
|
|
745
713
|
}, findFunctionEntries = async (functionsPath) => {
|
|
746
714
|
const entries = [];
|
|
747
715
|
for (const dir of FUNCTION_DIRS) {
|
|
748
|
-
const dirPath =
|
|
716
|
+
const dirPath = join5(functionsPath, dir);
|
|
749
717
|
try {
|
|
750
718
|
const items = await readdir(dirPath, { withFileTypes: true });
|
|
751
719
|
for (const item of items) {
|
|
752
720
|
if (item.isFile() && (item.name.endsWith(".ts") || item.name.endsWith(".tsx"))) {
|
|
753
|
-
const fullPath =
|
|
721
|
+
const fullPath = join5(dirPath, item.name);
|
|
754
722
|
const name = `${dir}/${item.name.replace(/\.tsx?$/, "")}`;
|
|
755
723
|
entries.push({
|
|
756
724
|
name,
|
|
@@ -770,7 +738,7 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
770
738
|
console.error('No local configuration found. Run "nulljs dev" first.');
|
|
771
739
|
return () => {};
|
|
772
740
|
}
|
|
773
|
-
const functionsPath =
|
|
741
|
+
const functionsPath = join5(srcPath, "function");
|
|
774
742
|
let entries = await findFunctionEntries(functionsPath);
|
|
775
743
|
let viteWatcher = null;
|
|
776
744
|
let isRestarting = false;
|
|
@@ -841,8 +809,8 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
841
809
|
viteWatcher = await startViteWatcher();
|
|
842
810
|
const dirWatchers = [];
|
|
843
811
|
for (const dir of FUNCTION_DIRS) {
|
|
844
|
-
const dirPath =
|
|
845
|
-
if (!
|
|
812
|
+
const dirPath = join5(functionsPath, dir);
|
|
813
|
+
if (!existsSync4(dirPath))
|
|
846
814
|
continue;
|
|
847
815
|
try {
|
|
848
816
|
const watcher = watch(dirPath, async (eventType, filename) => {
|
|
@@ -850,10 +818,10 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
850
818
|
return;
|
|
851
819
|
if (!filename.endsWith(".ts") && !filename.endsWith(".tsx"))
|
|
852
820
|
return;
|
|
853
|
-
const fullPath =
|
|
821
|
+
const fullPath = join5(dirPath, filename);
|
|
854
822
|
const entryName = `${dir}/${filename.replace(/\.tsx?$/, "")}`;
|
|
855
823
|
const existingEntry = entries.find((e) => e.name === entryName);
|
|
856
|
-
const fileExists =
|
|
824
|
+
const fileExists = existsSync4(fullPath);
|
|
857
825
|
if (!existingEntry && fileExists || existingEntry && !fileExists) {
|
|
858
826
|
await restartViteWatcher();
|
|
859
827
|
}
|
|
@@ -870,23 +838,25 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
870
838
|
}
|
|
871
839
|
};
|
|
872
840
|
}, forceDeployAll = async (srcPath, options = {}) => {
|
|
873
|
-
const { onDeploy, onComplete } = options;
|
|
841
|
+
const { onStart, onDeploy, onComplete } = options;
|
|
874
842
|
const config = readLocalConfig();
|
|
875
843
|
if (!config) {
|
|
876
844
|
throw new Error('No local configuration found. Run "nulljs dev" first.');
|
|
877
845
|
}
|
|
878
846
|
const privateKey = await loadPrivateKey(config);
|
|
879
847
|
deployedHashes2.clear();
|
|
880
|
-
const functionsPath =
|
|
848
|
+
const functionsPath = join5(srcPath, "function");
|
|
881
849
|
const entries = await findFunctionEntries(functionsPath);
|
|
882
|
-
const reactEntry =
|
|
883
|
-
const hasReact =
|
|
884
|
-
|
|
850
|
+
const reactEntry = join5(srcPath, "index.tsx");
|
|
851
|
+
const hasReact = existsSync4(reactEntry);
|
|
852
|
+
const total = entries.length + (hasReact ? 1 : 0);
|
|
885
853
|
let successful = 0;
|
|
886
854
|
let failed = 0;
|
|
855
|
+
let completed = 0;
|
|
887
856
|
const { deployFunction: deployFunction2, deployReact: deployReact2, clearDeployCache: clearDeployCache2 } = await Promise.resolve().then(() => (init_deploy2(), exports_deploy));
|
|
888
857
|
clearDeployCache2();
|
|
889
858
|
for (const entry of entries) {
|
|
859
|
+
onStart?.(entry.name, completed, total);
|
|
890
860
|
try {
|
|
891
861
|
await deployFunction2(entry.path, privateKey, config.api);
|
|
892
862
|
successful++;
|
|
@@ -895,9 +865,11 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
895
865
|
failed++;
|
|
896
866
|
onDeploy?.(entry.name, false, error instanceof Error ? error.message : String(error));
|
|
897
867
|
}
|
|
868
|
+
completed++;
|
|
898
869
|
}
|
|
899
870
|
if (hasReact) {
|
|
900
871
|
const fileName = "index.tsx";
|
|
872
|
+
onStart?.(fileName, completed, total);
|
|
901
873
|
try {
|
|
902
874
|
await deployReact2(reactEntry, privateKey, config.api);
|
|
903
875
|
successful++;
|
|
@@ -906,24 +878,25 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
906
878
|
failed++;
|
|
907
879
|
onDeploy?.(fileName, false, error instanceof Error ? error.message : String(error));
|
|
908
880
|
}
|
|
881
|
+
completed++;
|
|
909
882
|
}
|
|
910
883
|
onComplete?.(total, successful, failed);
|
|
911
884
|
};
|
|
912
885
|
var init_watcher = __esm(() => {
|
|
913
886
|
init_config();
|
|
914
887
|
init_bundle();
|
|
915
|
-
|
|
888
|
+
init_deploy();
|
|
916
889
|
FUNCTION_DIRS = ["api", "cron", "event"];
|
|
917
890
|
deployedHashes2 = new Map;
|
|
918
891
|
});
|
|
919
892
|
|
|
920
893
|
// src/lib/vite.ts
|
|
921
894
|
import { createServer } from "vite";
|
|
922
|
-
import { dirname as
|
|
895
|
+
import { dirname as dirname3, resolve, basename as basename3 } from "path";
|
|
923
896
|
import react3 from "@vitejs/plugin-react";
|
|
924
897
|
import tailwindcss2 from "@tailwindcss/vite";
|
|
925
898
|
import { writeFile, unlink } from "fs/promises";
|
|
926
|
-
import { existsSync as
|
|
899
|
+
import { existsSync as existsSync5 } from "fs";
|
|
927
900
|
var VITE_PORT = 5173, GATEWAY_PORT = 3001, createViteLogger = (onLog) => {
|
|
928
901
|
return {
|
|
929
902
|
info: (msg) => onLog?.(msg, "info"),
|
|
@@ -967,10 +940,10 @@ var VITE_PORT = 5173, GATEWAY_PORT = 3001, createViteLogger = (onLog) => {
|
|
|
967
940
|
</html>`;
|
|
968
941
|
}, startViteServer = async (options) => {
|
|
969
942
|
const indexTsxPath = resolve(options.srcDir, "index.tsx");
|
|
970
|
-
if (!
|
|
943
|
+
if (!existsSync5(indexTsxPath)) {
|
|
971
944
|
return null;
|
|
972
945
|
}
|
|
973
|
-
const projectDir =
|
|
946
|
+
const projectDir = dirname3(options.srcDir);
|
|
974
947
|
const tempIndexPath = resolve(projectDir, "index.html");
|
|
975
948
|
try {
|
|
976
949
|
const indexHtml = createTempIndexHtml(indexTsxPath, projectDir);
|
|
@@ -1011,235 +984,397 @@ var VITE_PORT = 5173, GATEWAY_PORT = 3001, createViteLogger = (onLog) => {
|
|
|
1011
984
|
};
|
|
1012
985
|
var init_vite = () => {};
|
|
1013
986
|
|
|
1014
|
-
// src/
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
case "error":
|
|
1019
|
-
return "red";
|
|
1020
|
-
case "warn":
|
|
1021
|
-
case "warning":
|
|
1022
|
-
return "yellow";
|
|
1023
|
-
case "info":
|
|
1024
|
-
return "cyan";
|
|
1025
|
-
case "debug":
|
|
1026
|
-
return "gray";
|
|
1027
|
-
default:
|
|
1028
|
-
return "white";
|
|
1029
|
-
}
|
|
987
|
+
// src/ui/utils/logs.ts
|
|
988
|
+
var LOG_LEVEL_COLORS, SYSTEM_LOG_LEVEL_COLORS, getLevelColor = (level, variant = "deployment") => {
|
|
989
|
+
const colors = variant === "system" ? SYSTEM_LOG_LEVEL_COLORS : LOG_LEVEL_COLORS;
|
|
990
|
+
return colors[level.toLowerCase()] || "white";
|
|
1030
991
|
}, sanitizeMessage = (message) => {
|
|
1031
992
|
return message.replace(/\x1b\[[0-9;]*m/g, "").replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
993
|
+
};
|
|
994
|
+
var init_logs = __esm(() => {
|
|
995
|
+
LOG_LEVEL_COLORS = {
|
|
996
|
+
error: "red",
|
|
997
|
+
warn: "yellow",
|
|
998
|
+
warning: "yellow",
|
|
999
|
+
info: "cyan",
|
|
1000
|
+
debug: "gray",
|
|
1001
|
+
trace: "gray"
|
|
1002
|
+
};
|
|
1003
|
+
SYSTEM_LOG_LEVEL_COLORS = {
|
|
1004
|
+
...LOG_LEVEL_COLORS,
|
|
1005
|
+
info: "green"
|
|
1006
|
+
};
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
// src/ui/utils/clipboard.ts
|
|
1010
|
+
import { exec } from "child_process";
|
|
1011
|
+
var copyToClipboard = (text) => {
|
|
1012
|
+
return new Promise((resolve2, reject) => {
|
|
1013
|
+
const proc = exec("pbcopy", (err) => {
|
|
1014
|
+
if (err)
|
|
1015
|
+
reject(err);
|
|
1016
|
+
else
|
|
1017
|
+
resolve2();
|
|
1018
|
+
});
|
|
1019
|
+
proc.stdin?.write(text);
|
|
1020
|
+
proc.stdin?.end();
|
|
1021
|
+
});
|
|
1022
|
+
};
|
|
1023
|
+
var init_clipboard = () => {};
|
|
1024
|
+
|
|
1025
|
+
// src/ui/utils/formatting.ts
|
|
1026
|
+
var getBinarySourceLabel = (source) => {
|
|
1027
|
+
switch (source) {
|
|
1028
|
+
case "local-debug":
|
|
1029
|
+
return "local debug";
|
|
1030
|
+
case "local-release":
|
|
1031
|
+
return "local release";
|
|
1032
|
+
case "local-env":
|
|
1033
|
+
return "env";
|
|
1034
|
+
case "npm":
|
|
1035
|
+
return "npm";
|
|
1036
|
+
default:
|
|
1037
|
+
return source;
|
|
1050
1038
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1039
|
+
};
|
|
1040
|
+
|
|
1041
|
+
// src/ui/utils/index.ts
|
|
1042
|
+
var init_utils = __esm(() => {
|
|
1043
|
+
init_logs();
|
|
1044
|
+
init_clipboard();
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// src/ui/components/layout/Header.tsx
|
|
1048
|
+
import { jsxDEV } from "@opentui/react/jsx-dev-runtime";
|
|
1049
|
+
var Header = (props) => {
|
|
1050
|
+
const { activeTab, functionCount, viteRunning, isDeploying, binarySource } = props;
|
|
1051
|
+
return /* @__PURE__ */ jsxDEV("box", {
|
|
1052
|
+
flexDirection: "column",
|
|
1053
|
+
children: /* @__PURE__ */ jsxDEV("box", {
|
|
1054
|
+
flexDirection: "row",
|
|
1055
|
+
justifyContent: "space-between",
|
|
1056
|
+
children: [
|
|
1057
|
+
/* @__PURE__ */ jsxDEV("box", {
|
|
1058
|
+
flexDirection: "row",
|
|
1059
|
+
gap: 2,
|
|
1063
1060
|
children: [
|
|
1064
|
-
/* @__PURE__ */ jsxDEV("
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
fg: getLevelColor(log.level),
|
|
1070
|
-
children: ` ${level} `
|
|
1061
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
1062
|
+
children: /* @__PURE__ */ jsxDEV("span", {
|
|
1063
|
+
fg: activeTab === "system" ? "cyan" : "gray",
|
|
1064
|
+
children: "[1] System Logs"
|
|
1065
|
+
}, undefined, false, undefined, this)
|
|
1071
1066
|
}, undefined, false, undefined, this),
|
|
1072
|
-
/* @__PURE__ */ jsxDEV("
|
|
1073
|
-
|
|
1074
|
-
|
|
1067
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
1068
|
+
children: /* @__PURE__ */ jsxDEV("span", {
|
|
1069
|
+
fg: "gray",
|
|
1070
|
+
children: " | "
|
|
1071
|
+
}, undefined, false, undefined, this)
|
|
1075
1072
|
}, undefined, false, undefined, this),
|
|
1076
|
-
/* @__PURE__ */ jsxDEV("
|
|
1077
|
-
|
|
1078
|
-
|
|
1073
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
1074
|
+
children: /* @__PURE__ */ jsxDEV("span", {
|
|
1075
|
+
fg: activeTab === "deployment" ? "cyan" : "gray",
|
|
1076
|
+
children: "[2] Deployment Logs"
|
|
1077
|
+
}, undefined, false, undefined, this)
|
|
1078
|
+
}, undefined, false, undefined, this)
|
|
1079
|
+
]
|
|
1080
|
+
}, undefined, true, undefined, this),
|
|
1081
|
+
/* @__PURE__ */ jsxDEV("box", {
|
|
1082
|
+
flexDirection: "row",
|
|
1083
|
+
gap: 2,
|
|
1084
|
+
children: [
|
|
1085
|
+
isDeploying && /* @__PURE__ */ jsxDEV("text", {
|
|
1086
|
+
children: /* @__PURE__ */ jsxDEV("span", {
|
|
1087
|
+
fg: "yellow",
|
|
1088
|
+
children: "deploying..."
|
|
1089
|
+
}, undefined, false, undefined, this)
|
|
1079
1090
|
}, undefined, false, undefined, this),
|
|
1080
|
-
/* @__PURE__ */ jsxDEV("
|
|
1081
|
-
children:
|
|
1091
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
1092
|
+
children: /* @__PURE__ */ jsxDEV("span", {
|
|
1093
|
+
fg: "gray",
|
|
1094
|
+
children: [
|
|
1095
|
+
functionCount,
|
|
1096
|
+
" functions | vite ",
|
|
1097
|
+
viteRunning ? "running" : "stopped",
|
|
1098
|
+
" |",
|
|
1099
|
+
" ",
|
|
1100
|
+
getBinarySourceLabel(binarySource),
|
|
1101
|
+
" | ?=help"
|
|
1102
|
+
]
|
|
1103
|
+
}, undefined, true, undefined, this)
|
|
1082
1104
|
}, undefined, false, undefined, this)
|
|
1083
1105
|
]
|
|
1084
1106
|
}, undefined, true, undefined, this)
|
|
1085
|
-
|
|
1086
|
-
})
|
|
1087
|
-
},
|
|
1107
|
+
]
|
|
1108
|
+
}, undefined, true, undefined, this)
|
|
1109
|
+
}, undefined, false, undefined, this);
|
|
1088
1110
|
};
|
|
1089
|
-
var
|
|
1111
|
+
var init_Header = __esm(() => {
|
|
1112
|
+
init_utils();
|
|
1113
|
+
});
|
|
1090
1114
|
|
|
1091
|
-
// src/
|
|
1115
|
+
// src/ui/constants/colors.ts
|
|
1116
|
+
var STATUS_COLORS, DEPLOY_COLORS;
|
|
1117
|
+
var init_colors = __esm(() => {
|
|
1118
|
+
STATUS_COLORS = {
|
|
1119
|
+
success: "#22c55e",
|
|
1120
|
+
warning: "#eab308",
|
|
1121
|
+
error: "#ef4444",
|
|
1122
|
+
info: "#3b82f6"
|
|
1123
|
+
};
|
|
1124
|
+
DEPLOY_COLORS = {
|
|
1125
|
+
success: "#22c55e",
|
|
1126
|
+
warning: "#eab308",
|
|
1127
|
+
error: "#ef4444",
|
|
1128
|
+
progress: "#3b82f6",
|
|
1129
|
+
muted: "#666"
|
|
1130
|
+
};
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
// src/ui/constants/keybindings.ts
|
|
1134
|
+
var KEYBINDINGS, KEYBINDING_DESCRIPTIONS;
|
|
1135
|
+
var init_keybindings = __esm(() => {
|
|
1136
|
+
KEYBINDINGS = {
|
|
1137
|
+
"1": { type: "NAVIGATE", tab: "system" },
|
|
1138
|
+
"2": { type: "NAVIGATE", tab: "deployment" },
|
|
1139
|
+
"\t": { type: "CYCLE_TAB" },
|
|
1140
|
+
h: { type: "NAVIGATE", tab: "system" },
|
|
1141
|
+
l: { type: "NAVIGATE", tab: "deployment" },
|
|
1142
|
+
g: { type: "SCROLL", to: "top" },
|
|
1143
|
+
G: { type: "SCROLL", to: "bottom" },
|
|
1144
|
+
"?": { type: "TOGGLE_HELP" },
|
|
1145
|
+
"\x1B": { type: "CLOSE_MODAL" },
|
|
1146
|
+
d: { type: "DEPLOY" },
|
|
1147
|
+
y: { type: "YANK" },
|
|
1148
|
+
x: { type: "CLEAN" }
|
|
1149
|
+
};
|
|
1150
|
+
KEYBINDING_DESCRIPTIONS = [
|
|
1151
|
+
{ section: "Navigation", bindings: [
|
|
1152
|
+
{ keys: "1/2", description: "Switch tabs" },
|
|
1153
|
+
{ keys: "h/l", description: "Previous/next tab" },
|
|
1154
|
+
{ keys: "Tab", description: "Cycle tabs" }
|
|
1155
|
+
] },
|
|
1156
|
+
{ section: "Scrolling", bindings: [
|
|
1157
|
+
{ keys: "j/k", description: "Scroll down/up" },
|
|
1158
|
+
{ keys: "g/G", description: "Top/bottom" }
|
|
1159
|
+
] },
|
|
1160
|
+
{ section: "Actions", bindings: [
|
|
1161
|
+
{ keys: "y", description: "Yank (copy) selection" },
|
|
1162
|
+
{ keys: "d", description: "Deploy all" },
|
|
1163
|
+
{ keys: "x", description: "Clean local data" },
|
|
1164
|
+
{ keys: "?", description: "Toggle help" },
|
|
1165
|
+
{ keys: "Esc", description: "Close help" }
|
|
1166
|
+
] }
|
|
1167
|
+
];
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
// src/ui/constants/index.ts
|
|
1171
|
+
var init_constants = __esm(() => {
|
|
1172
|
+
init_colors();
|
|
1173
|
+
init_keybindings();
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
// src/ui/components/deploy/DeployProgress.tsx
|
|
1092
1177
|
import { jsxDEV as jsxDEV2 } from "@opentui/react/jsx-dev-runtime";
|
|
1093
|
-
var
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
return message.replace(/\x1b\[[0-9;]*m/g, "").replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
1110
|
-
}, SystemLogsPane = (props) => {
|
|
1111
|
-
if (props.loading && props.logs.length === 0) {
|
|
1112
|
-
return /* @__PURE__ */ jsxDEV2("box", {
|
|
1113
|
-
flexDirection: "column",
|
|
1114
|
-
padding: 1,
|
|
1115
|
-
children: /* @__PURE__ */ jsxDEV2("text", {
|
|
1116
|
-
children: "Loading system logs..."
|
|
1117
|
-
}, undefined, false, undefined, this)
|
|
1118
|
-
}, undefined, false, undefined, this);
|
|
1119
|
-
}
|
|
1120
|
-
if (!props.loading && props.logs.length === 0) {
|
|
1121
|
-
return /* @__PURE__ */ jsxDEV2("box", {
|
|
1122
|
-
flexDirection: "column",
|
|
1123
|
-
padding: 1,
|
|
1124
|
-
children: /* @__PURE__ */ jsxDEV2("text", {
|
|
1125
|
-
children: "No system logs found"
|
|
1126
|
-
}, undefined, false, undefined, this)
|
|
1127
|
-
}, undefined, false, undefined, this);
|
|
1128
|
-
}
|
|
1129
|
-
const logsInOrder = [...props.logs].reverse();
|
|
1130
|
-
return /* @__PURE__ */ jsxDEV2("scrollbox", {
|
|
1131
|
-
focused: true,
|
|
1132
|
-
stickyStart: props.autoScroll ? "bottom" : "top",
|
|
1133
|
-
stickyScroll: props.autoScroll,
|
|
1134
|
-
children: logsInOrder.map((log) => {
|
|
1135
|
-
const time = new Date(log.timestamp).toLocaleTimeString();
|
|
1136
|
-
const timestamp = `[${time}]`;
|
|
1137
|
-
const level = log.level.toUpperCase();
|
|
1138
|
-
return /* @__PURE__ */ jsxDEV2("box", {
|
|
1139
|
-
flexDirection: "row",
|
|
1140
|
-
children: /* @__PURE__ */ jsxDEV2("text", {
|
|
1178
|
+
var DeployProgress = (props) => {
|
|
1179
|
+
const { currentFile, completedFiles, totalFiles } = props.progress;
|
|
1180
|
+
const completedCount = completedFiles.length;
|
|
1181
|
+
const failedCount = completedFiles.filter((f) => !f.success).length;
|
|
1182
|
+
return /* @__PURE__ */ jsxDEV2("box", {
|
|
1183
|
+
backgroundColor: "black",
|
|
1184
|
+
flexDirection: "row",
|
|
1185
|
+
paddingLeft: 1,
|
|
1186
|
+
paddingRight: 1,
|
|
1187
|
+
children: /* @__PURE__ */ jsxDEV2("text", {
|
|
1188
|
+
children: [
|
|
1189
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
1190
|
+
fg: DEPLOY_COLORS.progress,
|
|
1191
|
+
children: "\u27F3 Deploying"
|
|
1192
|
+
}, undefined, false, undefined, this),
|
|
1193
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
1141
1194
|
children: [
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1195
|
+
" ",
|
|
1196
|
+
currentFile
|
|
1197
|
+
]
|
|
1198
|
+
}, undefined, true, undefined, this),
|
|
1199
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
1200
|
+
fg: DEPLOY_COLORS.muted,
|
|
1201
|
+
children: [
|
|
1202
|
+
" ",
|
|
1203
|
+
"(",
|
|
1204
|
+
completedCount + 1,
|
|
1205
|
+
"/",
|
|
1206
|
+
totalFiles,
|
|
1207
|
+
")"
|
|
1208
|
+
]
|
|
1209
|
+
}, undefined, true, undefined, this),
|
|
1210
|
+
failedCount > 0 && /* @__PURE__ */ jsxDEV2("span", {
|
|
1211
|
+
fg: DEPLOY_COLORS.error,
|
|
1212
|
+
children: [
|
|
1213
|
+
" (",
|
|
1214
|
+
failedCount,
|
|
1215
|
+
" failed)"
|
|
1153
1216
|
]
|
|
1154
1217
|
}, undefined, true, undefined, this)
|
|
1155
|
-
|
|
1156
|
-
})
|
|
1157
|
-
},
|
|
1218
|
+
]
|
|
1219
|
+
}, undefined, true, undefined, this)
|
|
1220
|
+
}, undefined, false, undefined, this);
|
|
1158
1221
|
};
|
|
1159
|
-
var
|
|
1222
|
+
var init_DeployProgress = __esm(() => {
|
|
1223
|
+
init_constants();
|
|
1224
|
+
});
|
|
1160
1225
|
|
|
1161
|
-
// src/components/
|
|
1162
|
-
import { useEffect, useState
|
|
1163
|
-
import { jsxDEV as jsxDEV3 } from "@opentui/react/jsx-dev-runtime";
|
|
1164
|
-
var
|
|
1165
|
-
const
|
|
1166
|
-
const [
|
|
1167
|
-
const
|
|
1168
|
-
const
|
|
1169
|
-
const duration = 400;
|
|
1170
|
-
const frameRate = 16;
|
|
1226
|
+
// src/ui/components/deploy/DeployComplete.tsx
|
|
1227
|
+
import { useEffect, useState } from "react";
|
|
1228
|
+
import { jsxDEV as jsxDEV3, Fragment } from "@opentui/react/jsx-dev-runtime";
|
|
1229
|
+
var DeployComplete = (props) => {
|
|
1230
|
+
const { results, onComplete } = props;
|
|
1231
|
+
const [visible, setVisible] = useState(true);
|
|
1232
|
+
const successCount = results.filter((r) => r.success).length;
|
|
1233
|
+
const failedCount = results.filter((r) => !r.success).length;
|
|
1171
1234
|
useEffect(() => {
|
|
1172
|
-
|
|
1173
|
-
|
|
1235
|
+
const timer = setTimeout(() => {
|
|
1236
|
+
setVisible(false);
|
|
1237
|
+
onComplete();
|
|
1238
|
+
}, 3000);
|
|
1239
|
+
return () => clearTimeout(timer);
|
|
1240
|
+
}, [onComplete]);
|
|
1241
|
+
if (!visible || results.length === 0) {
|
|
1242
|
+
return null;
|
|
1243
|
+
}
|
|
1244
|
+
return /* @__PURE__ */ jsxDEV3("box", {
|
|
1245
|
+
backgroundColor: "black",
|
|
1246
|
+
flexDirection: "row",
|
|
1247
|
+
paddingLeft: 1,
|
|
1248
|
+
paddingRight: 1,
|
|
1249
|
+
children: /* @__PURE__ */ jsxDEV3("text", {
|
|
1250
|
+
children: failedCount === 0 ? /* @__PURE__ */ jsxDEV3("span", {
|
|
1251
|
+
fg: DEPLOY_COLORS.success,
|
|
1252
|
+
children: [
|
|
1253
|
+
"\u2713 Deployed ",
|
|
1254
|
+
successCount,
|
|
1255
|
+
" files"
|
|
1256
|
+
]
|
|
1257
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV3(Fragment, {
|
|
1258
|
+
children: [
|
|
1259
|
+
/* @__PURE__ */ jsxDEV3("span", {
|
|
1260
|
+
fg: DEPLOY_COLORS.success,
|
|
1261
|
+
children: [
|
|
1262
|
+
"\u2713 ",
|
|
1263
|
+
successCount,
|
|
1264
|
+
" deployed"
|
|
1265
|
+
]
|
|
1266
|
+
}, undefined, true, undefined, this),
|
|
1267
|
+
/* @__PURE__ */ jsxDEV3("span", {
|
|
1268
|
+
fg: DEPLOY_COLORS.error,
|
|
1269
|
+
children: [
|
|
1270
|
+
" \u2717 ",
|
|
1271
|
+
failedCount,
|
|
1272
|
+
" failed"
|
|
1273
|
+
]
|
|
1274
|
+
}, undefined, true, undefined, this)
|
|
1275
|
+
]
|
|
1276
|
+
}, undefined, true, undefined, this)
|
|
1277
|
+
}, undefined, false, undefined, this)
|
|
1278
|
+
}, undefined, false, undefined, this);
|
|
1279
|
+
};
|
|
1280
|
+
var init_DeployComplete = __esm(() => {
|
|
1281
|
+
init_constants();
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
// src/ui/components/deploy/CleanProgress.tsx
|
|
1285
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1286
|
+
import { jsxDEV as jsxDEV4 } from "@opentui/react/jsx-dev-runtime";
|
|
1287
|
+
var CleanProgress = (props) => {
|
|
1288
|
+
const { items, onRestartServer, onComplete } = props;
|
|
1289
|
+
const [currentIndex, setCurrentIndex] = useState2(0);
|
|
1290
|
+
const [phase, setPhase] = useState2("deleting");
|
|
1291
|
+
useEffect2(() => {
|
|
1292
|
+
if (items.length === 0) {
|
|
1293
|
+
onComplete();
|
|
1174
1294
|
return;
|
|
1175
1295
|
}
|
|
1176
|
-
if (
|
|
1296
|
+
if (phase === "deleting") {
|
|
1297
|
+
if (currentIndex >= items.length) {
|
|
1298
|
+
setPhase("restarting");
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
const timer = setTimeout(() => {
|
|
1302
|
+
setCurrentIndex((prev) => prev + 1);
|
|
1303
|
+
}, 300);
|
|
1304
|
+
return () => clearTimeout(timer);
|
|
1305
|
+
}
|
|
1306
|
+
if (phase === "restarting") {
|
|
1307
|
+
onRestartServer().then(() => {
|
|
1308
|
+
setPhase("complete");
|
|
1309
|
+
});
|
|
1177
1310
|
return;
|
|
1178
1311
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
if (newProgress >= 100) {
|
|
1186
|
-
if (intervalRef.current) {
|
|
1187
|
-
clearInterval(intervalRef.current);
|
|
1188
|
-
intervalRef.current = null;
|
|
1189
|
-
}
|
|
1190
|
-
setTimeout(() => {
|
|
1191
|
-
if (currentIndex >= props.deployed.length - 1) {
|
|
1192
|
-
completedRef.current = true;
|
|
1193
|
-
props.onComplete();
|
|
1194
|
-
} else {
|
|
1195
|
-
setCurrentIndex((prev) => prev + 1);
|
|
1196
|
-
}
|
|
1197
|
-
}, 150);
|
|
1198
|
-
}
|
|
1199
|
-
}, frameRate);
|
|
1200
|
-
return () => {
|
|
1201
|
-
if (intervalRef.current) {
|
|
1202
|
-
clearInterval(intervalRef.current);
|
|
1203
|
-
}
|
|
1204
|
-
};
|
|
1205
|
-
}, [currentIndex, props.deployed.length, props.onComplete]);
|
|
1206
|
-
const current = props.deployed[currentIndex];
|
|
1207
|
-
if (props.deployed.length === 0 || !current || completedRef.current) {
|
|
1312
|
+
if (phase === "complete") {
|
|
1313
|
+
const timer = setTimeout(onComplete, 2000);
|
|
1314
|
+
return () => clearTimeout(timer);
|
|
1315
|
+
}
|
|
1316
|
+
}, [currentIndex, items.length, phase, onRestartServer, onComplete]);
|
|
1317
|
+
if (items.length === 0) {
|
|
1208
1318
|
return null;
|
|
1209
1319
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1320
|
+
if (phase === "complete") {
|
|
1321
|
+
return /* @__PURE__ */ jsxDEV4("box", {
|
|
1322
|
+
backgroundColor: "black",
|
|
1323
|
+
flexDirection: "row",
|
|
1324
|
+
paddingLeft: 1,
|
|
1325
|
+
paddingRight: 1,
|
|
1326
|
+
children: /* @__PURE__ */ jsxDEV4("text", {
|
|
1327
|
+
children: /* @__PURE__ */ jsxDEV4("span", {
|
|
1328
|
+
fg: DEPLOY_COLORS.success,
|
|
1329
|
+
children: [
|
|
1330
|
+
"\u2713 Cleaned ",
|
|
1331
|
+
items.length,
|
|
1332
|
+
" items, server restarted"
|
|
1333
|
+
]
|
|
1334
|
+
}, undefined, true, undefined, this)
|
|
1335
|
+
}, undefined, false, undefined, this)
|
|
1336
|
+
}, undefined, false, undefined, this);
|
|
1337
|
+
}
|
|
1338
|
+
if (phase === "restarting") {
|
|
1339
|
+
return /* @__PURE__ */ jsxDEV4("box", {
|
|
1340
|
+
backgroundColor: "black",
|
|
1341
|
+
flexDirection: "row",
|
|
1342
|
+
paddingLeft: 1,
|
|
1343
|
+
paddingRight: 1,
|
|
1344
|
+
children: /* @__PURE__ */ jsxDEV4("text", {
|
|
1345
|
+
children: /* @__PURE__ */ jsxDEV4("span", {
|
|
1346
|
+
fg: DEPLOY_COLORS.warning,
|
|
1347
|
+
children: "\u27F3 Restarting server..."
|
|
1348
|
+
}, undefined, false, undefined, this)
|
|
1349
|
+
}, undefined, false, undefined, this)
|
|
1350
|
+
}, undefined, false, undefined, this);
|
|
1351
|
+
}
|
|
1352
|
+
const current = items[currentIndex];
|
|
1353
|
+
return /* @__PURE__ */ jsxDEV4("box", {
|
|
1215
1354
|
backgroundColor: "black",
|
|
1216
1355
|
flexDirection: "row",
|
|
1217
1356
|
paddingLeft: 1,
|
|
1218
1357
|
paddingRight: 1,
|
|
1219
|
-
children: /* @__PURE__ */
|
|
1358
|
+
children: /* @__PURE__ */ jsxDEV4("text", {
|
|
1220
1359
|
children: [
|
|
1221
|
-
/* @__PURE__ */
|
|
1222
|
-
fg:
|
|
1223
|
-
children:
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
current.success ? "\u2713" : "\u2717"
|
|
1227
|
-
]
|
|
1228
|
-
}, undefined, true, undefined, this),
|
|
1229
|
-
/* @__PURE__ */ jsxDEV3("span", {
|
|
1360
|
+
/* @__PURE__ */ jsxDEV4("span", {
|
|
1361
|
+
fg: DEPLOY_COLORS.error,
|
|
1362
|
+
children: "\u2717 Deleting"
|
|
1363
|
+
}, undefined, false, undefined, this),
|
|
1364
|
+
/* @__PURE__ */ jsxDEV4("span", {
|
|
1230
1365
|
children: [
|
|
1231
1366
|
" ",
|
|
1232
|
-
current
|
|
1367
|
+
current
|
|
1233
1368
|
]
|
|
1234
1369
|
}, undefined, true, undefined, this),
|
|
1235
|
-
|
|
1236
|
-
fg:
|
|
1370
|
+
/* @__PURE__ */ jsxDEV4("span", {
|
|
1371
|
+
fg: DEPLOY_COLORS.muted,
|
|
1237
1372
|
children: [
|
|
1238
1373
|
" ",
|
|
1239
1374
|
"(",
|
|
1240
1375
|
currentIndex + 1,
|
|
1241
1376
|
"/",
|
|
1242
|
-
|
|
1377
|
+
items.length,
|
|
1243
1378
|
")"
|
|
1244
1379
|
]
|
|
1245
1380
|
}, undefined, true, undefined, this)
|
|
@@ -1247,235 +1382,436 @@ var DeployAnimation = (props) => {
|
|
|
1247
1382
|
}, undefined, true, undefined, this)
|
|
1248
1383
|
}, undefined, false, undefined, this);
|
|
1249
1384
|
};
|
|
1250
|
-
var
|
|
1385
|
+
var init_CleanProgress = __esm(() => {
|
|
1386
|
+
init_constants();
|
|
1387
|
+
});
|
|
1251
1388
|
|
|
1252
|
-
// src/components/
|
|
1253
|
-
import { jsxDEV as
|
|
1254
|
-
var
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
case "local-release":
|
|
1259
|
-
return "local (release)";
|
|
1260
|
-
case "local-env":
|
|
1261
|
-
return "local (env)";
|
|
1262
|
-
case "npm":
|
|
1263
|
-
return "npm";
|
|
1389
|
+
// src/ui/components/layout/StatusBar.tsx
|
|
1390
|
+
import { jsxDEV as jsxDEV5 } from "@opentui/react/jsx-dev-runtime";
|
|
1391
|
+
var StatusBar = (props) => {
|
|
1392
|
+
const { deployProgress, deployComplete, cleanProgress, message } = props;
|
|
1393
|
+
if (!deployProgress && !deployComplete?.results.length && !cleanProgress && !message) {
|
|
1394
|
+
return null;
|
|
1264
1395
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1396
|
+
if (deployProgress) {
|
|
1397
|
+
return /* @__PURE__ */ jsxDEV5("box", {
|
|
1398
|
+
position: "absolute",
|
|
1399
|
+
bottom: 0,
|
|
1400
|
+
left: 0,
|
|
1401
|
+
width: "100%",
|
|
1402
|
+
flexDirection: "column",
|
|
1403
|
+
children: /* @__PURE__ */ jsxDEV5(DeployProgress, {
|
|
1404
|
+
progress: deployProgress
|
|
1405
|
+
}, undefined, false, undefined, this)
|
|
1406
|
+
}, undefined, false, undefined, this);
|
|
1407
|
+
}
|
|
1408
|
+
if (deployComplete && deployComplete.results.length > 0) {
|
|
1409
|
+
return /* @__PURE__ */ jsxDEV5("box", {
|
|
1410
|
+
position: "absolute",
|
|
1411
|
+
bottom: 0,
|
|
1412
|
+
left: 0,
|
|
1413
|
+
width: "100%",
|
|
1414
|
+
flexDirection: "column",
|
|
1415
|
+
children: /* @__PURE__ */ jsxDEV5(DeployComplete, {
|
|
1416
|
+
results: deployComplete.results,
|
|
1417
|
+
onComplete: deployComplete.onComplete
|
|
1418
|
+
}, undefined, false, undefined, this)
|
|
1419
|
+
}, undefined, false, undefined, this);
|
|
1420
|
+
}
|
|
1421
|
+
if (cleanProgress && cleanProgress.items.length > 0) {
|
|
1422
|
+
return /* @__PURE__ */ jsxDEV5("box", {
|
|
1423
|
+
position: "absolute",
|
|
1424
|
+
bottom: 0,
|
|
1425
|
+
left: 0,
|
|
1426
|
+
width: "100%",
|
|
1427
|
+
flexDirection: "column",
|
|
1428
|
+
children: /* @__PURE__ */ jsxDEV5(CleanProgress, {
|
|
1429
|
+
items: cleanProgress.items,
|
|
1430
|
+
onRestartServer: cleanProgress.onRestartServer,
|
|
1431
|
+
onComplete: cleanProgress.onComplete
|
|
1432
|
+
}, undefined, false, undefined, this)
|
|
1433
|
+
}, undefined, false, undefined, this);
|
|
1434
|
+
}
|
|
1435
|
+
if (message) {
|
|
1436
|
+
return /* @__PURE__ */ jsxDEV5("box", {
|
|
1437
|
+
position: "absolute",
|
|
1438
|
+
bottom: 0,
|
|
1439
|
+
left: 0,
|
|
1440
|
+
width: "100%",
|
|
1441
|
+
flexDirection: "column",
|
|
1442
|
+
children: /* @__PURE__ */ jsxDEV5("box", {
|
|
1443
|
+
backgroundColor: "black",
|
|
1444
|
+
paddingLeft: 1,
|
|
1445
|
+
paddingRight: 1,
|
|
1446
|
+
children: /* @__PURE__ */ jsxDEV5("text", {
|
|
1447
|
+
children: [
|
|
1448
|
+
/* @__PURE__ */ jsxDEV5("span", {
|
|
1449
|
+
fg: STATUS_COLORS[message.type],
|
|
1450
|
+
children: [
|
|
1451
|
+
message.icon,
|
|
1452
|
+
" ",
|
|
1453
|
+
message.text
|
|
1454
|
+
]
|
|
1455
|
+
}, undefined, true, undefined, this),
|
|
1456
|
+
message.detail && /* @__PURE__ */ jsxDEV5("span", {
|
|
1457
|
+
fg: "#666",
|
|
1458
|
+
children: [
|
|
1459
|
+
" ",
|
|
1460
|
+
message.detail
|
|
1461
|
+
]
|
|
1462
|
+
}, undefined, true, undefined, this)
|
|
1463
|
+
]
|
|
1464
|
+
}, undefined, true, undefined, this)
|
|
1465
|
+
}, undefined, false, undefined, this)
|
|
1466
|
+
}, undefined, false, undefined, this);
|
|
1467
|
+
}
|
|
1468
|
+
return null;
|
|
1469
|
+
};
|
|
1470
|
+
var init_StatusBar = __esm(() => {
|
|
1471
|
+
init_DeployProgress();
|
|
1472
|
+
init_DeployComplete();
|
|
1473
|
+
init_CleanProgress();
|
|
1474
|
+
init_constants();
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
// src/ui/components/layout/LogPane.tsx
|
|
1478
|
+
import { jsxDEV as jsxDEV6 } from "@opentui/react/jsx-dev-runtime";
|
|
1479
|
+
var LogPane = (props) => {
|
|
1480
|
+
const { logs: logs2, loading, autoScroll, jumpTrigger, emptyMessage, loadingMessage, children } = props;
|
|
1481
|
+
if (loading && logs2.length === 0) {
|
|
1482
|
+
return /* @__PURE__ */ jsxDEV6("box", {
|
|
1483
|
+
flexDirection: "column",
|
|
1484
|
+
padding: 1,
|
|
1485
|
+
children: /* @__PURE__ */ jsxDEV6("text", {
|
|
1486
|
+
children: loadingMessage
|
|
1487
|
+
}, undefined, false, undefined, this)
|
|
1488
|
+
}, undefined, false, undefined, this);
|
|
1489
|
+
}
|
|
1490
|
+
if (!loading && logs2.length === 0) {
|
|
1491
|
+
return /* @__PURE__ */ jsxDEV6("box", {
|
|
1492
|
+
flexDirection: "column",
|
|
1493
|
+
padding: 1,
|
|
1494
|
+
children: /* @__PURE__ */ jsxDEV6("text", {
|
|
1495
|
+
children: emptyMessage
|
|
1496
|
+
}, undefined, false, undefined, this)
|
|
1497
|
+
}, undefined, false, undefined, this);
|
|
1498
|
+
}
|
|
1499
|
+
return /* @__PURE__ */ jsxDEV6("scrollbox", {
|
|
1500
|
+
focused: true,
|
|
1501
|
+
flexGrow: 1,
|
|
1502
|
+
height: "100%",
|
|
1503
|
+
stickyStart: autoScroll ? "bottom" : "top",
|
|
1504
|
+
stickyScroll: autoScroll,
|
|
1505
|
+
children
|
|
1506
|
+
}, `${autoScroll}-${jumpTrigger}`, false, undefined, this);
|
|
1507
|
+
};
|
|
1508
|
+
var init_LogPane = () => {};
|
|
1509
|
+
|
|
1510
|
+
// src/ui/components/layout/index.ts
|
|
1511
|
+
var init_layout = __esm(() => {
|
|
1512
|
+
init_Header();
|
|
1513
|
+
init_StatusBar();
|
|
1514
|
+
init_LogPane();
|
|
1515
|
+
});
|
|
1516
|
+
|
|
1517
|
+
// src/ui/components/logs/LogEntry.tsx
|
|
1518
|
+
import { jsxDEV as jsxDEV7 } from "@opentui/react/jsx-dev-runtime";
|
|
1519
|
+
var LogEntry = (props) => {
|
|
1520
|
+
const { timestamp, level, message, variant = "deployment", deploymentName, trigger } = props;
|
|
1521
|
+
return /* @__PURE__ */ jsxDEV7("box", {
|
|
1268
1522
|
flexDirection: "row",
|
|
1269
|
-
|
|
1523
|
+
children: /* @__PURE__ */ jsxDEV7("text", {
|
|
1524
|
+
children: [
|
|
1525
|
+
/* @__PURE__ */ jsxDEV7("span", {
|
|
1526
|
+
fg: "gray",
|
|
1527
|
+
children: [
|
|
1528
|
+
"[",
|
|
1529
|
+
timestamp,
|
|
1530
|
+
"]"
|
|
1531
|
+
]
|
|
1532
|
+
}, undefined, true, undefined, this),
|
|
1533
|
+
/* @__PURE__ */ jsxDEV7("span", {
|
|
1534
|
+
fg: getLevelColor(level, variant),
|
|
1535
|
+
children: ` ${level.toUpperCase()} `
|
|
1536
|
+
}, undefined, false, undefined, this),
|
|
1537
|
+
deploymentName && /* @__PURE__ */ jsxDEV7("span", {
|
|
1538
|
+
fg: "blue",
|
|
1539
|
+
children: `(${deploymentName})`
|
|
1540
|
+
}, undefined, false, undefined, this),
|
|
1541
|
+
trigger && /* @__PURE__ */ jsxDEV7("span", {
|
|
1542
|
+
fg: "cyan",
|
|
1543
|
+
children: ` ${trigger} `
|
|
1544
|
+
}, undefined, false, undefined, this),
|
|
1545
|
+
/* @__PURE__ */ jsxDEV7("span", {
|
|
1546
|
+
children: sanitizeMessage(message)
|
|
1547
|
+
}, undefined, false, undefined, this)
|
|
1548
|
+
]
|
|
1549
|
+
}, undefined, true, undefined, this)
|
|
1550
|
+
}, undefined, false, undefined, this);
|
|
1551
|
+
};
|
|
1552
|
+
var init_LogEntry = __esm(() => {
|
|
1553
|
+
init_utils();
|
|
1554
|
+
});
|
|
1555
|
+
|
|
1556
|
+
// src/ui/components/logs/SystemLogs.tsx
|
|
1557
|
+
import { jsxDEV as jsxDEV8 } from "@opentui/react/jsx-dev-runtime";
|
|
1558
|
+
var SystemLogs = (props) => {
|
|
1559
|
+
const { logs: logs2, loading, autoScroll, jumpTrigger } = props;
|
|
1560
|
+
const logsInOrder = [...logs2].reverse();
|
|
1561
|
+
return /* @__PURE__ */ jsxDEV8(LogPane, {
|
|
1562
|
+
logs: logs2,
|
|
1563
|
+
loading,
|
|
1564
|
+
autoScroll,
|
|
1565
|
+
jumpTrigger,
|
|
1566
|
+
loadingMessage: "Loading system logs...",
|
|
1567
|
+
emptyMessage: "No system logs found",
|
|
1568
|
+
children: logsInOrder.map((log) => /* @__PURE__ */ jsxDEV8(LogEntry, {
|
|
1569
|
+
timestamp: new Date(log.timestamp).toLocaleTimeString(),
|
|
1570
|
+
level: log.level,
|
|
1571
|
+
message: log.message,
|
|
1572
|
+
variant: "system"
|
|
1573
|
+
}, log.id, false, undefined, this))
|
|
1574
|
+
}, undefined, false, undefined, this);
|
|
1575
|
+
};
|
|
1576
|
+
var init_SystemLogs = __esm(() => {
|
|
1577
|
+
init_layout();
|
|
1578
|
+
init_LogEntry();
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
// src/ui/components/logs/DeploymentLogs.tsx
|
|
1582
|
+
import { jsxDEV as jsxDEV9 } from "@opentui/react/jsx-dev-runtime";
|
|
1583
|
+
var DeploymentLogs = (props) => {
|
|
1584
|
+
const { logs: logs2, loading, autoScroll, jumpTrigger } = props;
|
|
1585
|
+
const logsInOrder = [...logs2].reverse();
|
|
1586
|
+
return /* @__PURE__ */ jsxDEV9(LogPane, {
|
|
1587
|
+
logs: logs2,
|
|
1588
|
+
loading,
|
|
1589
|
+
autoScroll,
|
|
1590
|
+
jumpTrigger,
|
|
1591
|
+
loadingMessage: "Loading deployment logs...",
|
|
1592
|
+
emptyMessage: "No deployment logs found",
|
|
1593
|
+
children: logsInOrder.map((log) => /* @__PURE__ */ jsxDEV9(LogEntry, {
|
|
1594
|
+
timestamp: new Date(log.time).toLocaleTimeString(),
|
|
1595
|
+
level: log.level,
|
|
1596
|
+
message: log.message,
|
|
1597
|
+
variant: "deployment",
|
|
1598
|
+
deploymentName: log.deployment_name,
|
|
1599
|
+
trigger: log.trigger
|
|
1600
|
+
}, log.id, false, undefined, this))
|
|
1601
|
+
}, undefined, false, undefined, this);
|
|
1602
|
+
};
|
|
1603
|
+
var init_DeploymentLogs = __esm(() => {
|
|
1604
|
+
init_layout();
|
|
1605
|
+
init_LogEntry();
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
// src/ui/components/logs/index.ts
|
|
1609
|
+
var init_logs2 = __esm(() => {
|
|
1610
|
+
init_LogEntry();
|
|
1611
|
+
init_SystemLogs();
|
|
1612
|
+
init_DeploymentLogs();
|
|
1613
|
+
});
|
|
1614
|
+
|
|
1615
|
+
// src/ui/components/modal/Modal.tsx
|
|
1616
|
+
import { jsxDEV as jsxDEV10 } from "@opentui/react/jsx-dev-runtime";
|
|
1617
|
+
var Modal = (props) => {
|
|
1618
|
+
const { visible, width = 50, height = 20, children } = props;
|
|
1619
|
+
if (!visible)
|
|
1620
|
+
return null;
|
|
1621
|
+
return /* @__PURE__ */ jsxDEV10("box", {
|
|
1622
|
+
position: "absolute",
|
|
1623
|
+
top: 0,
|
|
1624
|
+
left: 0,
|
|
1625
|
+
width: "100%",
|
|
1626
|
+
height: "100%",
|
|
1627
|
+
backgroundColor: "black",
|
|
1628
|
+
opacity: 0.9,
|
|
1629
|
+
justifyContent: "center",
|
|
1630
|
+
alignItems: "center",
|
|
1631
|
+
children: /* @__PURE__ */ jsxDEV10("box", {
|
|
1632
|
+
width,
|
|
1633
|
+
height,
|
|
1634
|
+
border: true,
|
|
1635
|
+
borderStyle: "rounded",
|
|
1636
|
+
flexDirection: "column",
|
|
1637
|
+
padding: 1,
|
|
1638
|
+
backgroundColor: "black",
|
|
1639
|
+
children
|
|
1640
|
+
}, undefined, false, undefined, this)
|
|
1641
|
+
}, undefined, false, undefined, this);
|
|
1642
|
+
};
|
|
1643
|
+
var init_Modal = () => {};
|
|
1644
|
+
|
|
1645
|
+
// src/ui/components/modal/HelpModal.tsx
|
|
1646
|
+
import { jsxDEV as jsxDEV11 } from "@opentui/react/jsx-dev-runtime";
|
|
1647
|
+
var KeyBinding = ({ keys, description }) => /* @__PURE__ */ jsxDEV11("text", {
|
|
1648
|
+
children: [
|
|
1649
|
+
" ",
|
|
1650
|
+
/* @__PURE__ */ jsxDEV11("span", {
|
|
1651
|
+
fg: "green",
|
|
1652
|
+
children: keys
|
|
1653
|
+
}, undefined, false, undefined, this),
|
|
1654
|
+
/* @__PURE__ */ jsxDEV11("span", {
|
|
1655
|
+
fg: "gray",
|
|
1656
|
+
children: " - "
|
|
1657
|
+
}, undefined, false, undefined, this),
|
|
1658
|
+
description
|
|
1659
|
+
]
|
|
1660
|
+
}, undefined, true, undefined, this), Section = ({ title }) => /* @__PURE__ */ jsxDEV11("text", {
|
|
1661
|
+
children: /* @__PURE__ */ jsxDEV11("span", {
|
|
1662
|
+
fg: "gray",
|
|
1663
|
+
children: title
|
|
1664
|
+
}, undefined, false, undefined, this)
|
|
1665
|
+
}, undefined, false, undefined, this), HelpModal = ({ visible }) => {
|
|
1666
|
+
return /* @__PURE__ */ jsxDEV11(Modal, {
|
|
1667
|
+
visible,
|
|
1668
|
+
width: 50,
|
|
1669
|
+
height: 20,
|
|
1270
1670
|
children: [
|
|
1271
|
-
/* @__PURE__ */
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
/* @__PURE__ */
|
|
1275
|
-
|
|
1276
|
-
content: "[1] System Logs"
|
|
1277
|
-
}, undefined, false, undefined, this),
|
|
1278
|
-
/* @__PURE__ */ jsxDEV4("text", {
|
|
1279
|
-
content: " | "
|
|
1280
|
-
}, undefined, false, undefined, this),
|
|
1281
|
-
/* @__PURE__ */ jsxDEV4("text", {
|
|
1282
|
-
fg: activeTab === "deployment" ? "cyan" : undefined,
|
|
1283
|
-
content: "[2] Deployment Logs"
|
|
1671
|
+
/* @__PURE__ */ jsxDEV11("text", {
|
|
1672
|
+
children: /* @__PURE__ */ jsxDEV11("span", {
|
|
1673
|
+
fg: "cyan",
|
|
1674
|
+
children: /* @__PURE__ */ jsxDEV11("strong", {
|
|
1675
|
+
children: "Keyboard Shortcuts"
|
|
1284
1676
|
}, undefined, false, undefined, this)
|
|
1285
|
-
|
|
1286
|
-
}, undefined,
|
|
1287
|
-
/* @__PURE__ */
|
|
1288
|
-
|
|
1677
|
+
}, undefined, false, undefined, this)
|
|
1678
|
+
}, undefined, false, undefined, this),
|
|
1679
|
+
/* @__PURE__ */ jsxDEV11("text", {}, undefined, false, undefined, this),
|
|
1680
|
+
KEYBINDING_DESCRIPTIONS.map((section, sectionIndex) => /* @__PURE__ */ jsxDEV11("box", {
|
|
1681
|
+
flexDirection: "column",
|
|
1289
1682
|
children: [
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV4("text", {
|
|
1294
|
-
fg: functionCount > 0 ? "green" : "yellow",
|
|
1295
|
-
content: "\u25CF watching"
|
|
1296
|
-
}, undefined, false, undefined, this),
|
|
1297
|
-
/* @__PURE__ */ jsxDEV4("text", {
|
|
1298
|
-
fg: "gray",
|
|
1299
|
-
content: ` (${functionCount} functions)`
|
|
1683
|
+
sectionIndex > 0 && /* @__PURE__ */ jsxDEV11("text", {}, undefined, false, undefined, this),
|
|
1684
|
+
/* @__PURE__ */ jsxDEV11(Section, {
|
|
1685
|
+
title: section.section
|
|
1300
1686
|
}, undefined, false, undefined, this),
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
content: " | vite: "
|
|
1306
|
-
}, undefined, false, undefined, this),
|
|
1307
|
-
/* @__PURE__ */ jsxDEV4("text", {
|
|
1308
|
-
fg: "magenta",
|
|
1309
|
-
content: ":5173"
|
|
1310
|
-
}, undefined, false, undefined, this)
|
|
1311
|
-
]
|
|
1312
|
-
}, undefined, true, undefined, this),
|
|
1313
|
-
/* @__PURE__ */ jsxDEV4("text", {
|
|
1314
|
-
fg: "gray",
|
|
1315
|
-
content: " | server: "
|
|
1316
|
-
}, undefined, false, undefined, this),
|
|
1317
|
-
/* @__PURE__ */ jsxDEV4("text", {
|
|
1318
|
-
fg: binarySource.startsWith("local") ? "yellow" : "green",
|
|
1319
|
-
content: getBinarySourceLabel(binarySource)
|
|
1320
|
-
}, undefined, false, undefined, this)
|
|
1687
|
+
section.bindings.map((binding) => /* @__PURE__ */ jsxDEV11(KeyBinding, {
|
|
1688
|
+
keys: binding.keys,
|
|
1689
|
+
description: binding.description
|
|
1690
|
+
}, binding.keys, false, undefined, this))
|
|
1321
1691
|
]
|
|
1322
|
-
},
|
|
1692
|
+
}, section.section, true, undefined, this))
|
|
1323
1693
|
]
|
|
1324
1694
|
}, undefined, true, undefined, this);
|
|
1325
1695
|
};
|
|
1326
|
-
var
|
|
1696
|
+
var init_HelpModal = __esm(() => {
|
|
1697
|
+
init_Modal();
|
|
1698
|
+
init_constants();
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
// src/ui/components/modal/index.ts
|
|
1702
|
+
var init_modal = __esm(() => {
|
|
1703
|
+
init_Modal();
|
|
1704
|
+
init_HelpModal();
|
|
1705
|
+
});
|
|
1706
|
+
// ../../packages/api/types/index.ts
|
|
1707
|
+
var init_types = () => {};
|
|
1708
|
+
|
|
1709
|
+
// ../../packages/api/actions/client.ts
|
|
1710
|
+
var buildUrl = (path, options) => {
|
|
1711
|
+
let fullPath = options?.url ? `${options.url}${path}` : path;
|
|
1712
|
+
if (options?.query) {
|
|
1713
|
+
const params = [];
|
|
1714
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
1715
|
+
if (value === undefined || value === null)
|
|
1716
|
+
continue;
|
|
1717
|
+
if (Array.isArray(value)) {
|
|
1718
|
+
params.push(`${key}=${value.join(",")}`);
|
|
1719
|
+
} else {
|
|
1720
|
+
params.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
const queryString = params.join("&");
|
|
1724
|
+
if (queryString) {
|
|
1725
|
+
fullPath = `${fullPath}?${queryString}`;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
return fullPath;
|
|
1729
|
+
}, apiGet = async (path, options) => {
|
|
1730
|
+
const url = options?.url ? `${options.url}${path}` : path;
|
|
1731
|
+
const headers = {};
|
|
1732
|
+
if (options?.publicKey) {
|
|
1733
|
+
headers["x-public-key"] = options.publicKey;
|
|
1734
|
+
}
|
|
1735
|
+
const response = await fetch(url, { headers });
|
|
1736
|
+
if (!response.ok) {
|
|
1737
|
+
const message = await response.text().catch(() => "");
|
|
1738
|
+
throw new Error(`HTTP error! status: ${response.status}${message ? ` message: ${message}` : ""}`);
|
|
1739
|
+
}
|
|
1740
|
+
return response.json();
|
|
1741
|
+
};
|
|
1327
1742
|
|
|
1328
|
-
//
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
description
|
|
1342
|
-
]
|
|
1343
|
-
}, undefined, true, undefined, this), Section = ({ title }) => /* @__PURE__ */ jsxDEV5("text", {
|
|
1344
|
-
children: /* @__PURE__ */ jsxDEV5("span", {
|
|
1345
|
-
fg: "gray",
|
|
1346
|
-
children: title
|
|
1347
|
-
}, undefined, false, undefined, this)
|
|
1348
|
-
}, undefined, false, undefined, this), HelpModal = ({ visible }) => {
|
|
1349
|
-
if (!visible)
|
|
1350
|
-
return null;
|
|
1351
|
-
return /* @__PURE__ */ jsxDEV5("box", {
|
|
1352
|
-
position: "absolute",
|
|
1353
|
-
top: 0,
|
|
1354
|
-
left: 0,
|
|
1355
|
-
width: "100%",
|
|
1356
|
-
height: "100%",
|
|
1357
|
-
backgroundColor: "black",
|
|
1358
|
-
opacity: 0.9,
|
|
1359
|
-
justifyContent: "center",
|
|
1360
|
-
alignItems: "center",
|
|
1361
|
-
children: /* @__PURE__ */ jsxDEV5("box", {
|
|
1362
|
-
width: 50,
|
|
1363
|
-
height: 18,
|
|
1364
|
-
border: true,
|
|
1365
|
-
borderStyle: "rounded",
|
|
1366
|
-
flexDirection: "column",
|
|
1367
|
-
padding: 1,
|
|
1368
|
-
backgroundColor: "black",
|
|
1369
|
-
children: [
|
|
1370
|
-
/* @__PURE__ */ jsxDEV5("text", {
|
|
1371
|
-
children: /* @__PURE__ */ jsxDEV5("span", {
|
|
1372
|
-
fg: "cyan",
|
|
1373
|
-
children: /* @__PURE__ */ jsxDEV5("strong", {
|
|
1374
|
-
children: "Keyboard Shortcuts"
|
|
1375
|
-
}, undefined, false, undefined, this)
|
|
1376
|
-
}, undefined, false, undefined, this)
|
|
1377
|
-
}, undefined, false, undefined, this),
|
|
1378
|
-
/* @__PURE__ */ jsxDEV5("text", {}, undefined, false, undefined, this),
|
|
1379
|
-
/* @__PURE__ */ jsxDEV5(Section, {
|
|
1380
|
-
title: "Navigation"
|
|
1381
|
-
}, undefined, false, undefined, this),
|
|
1382
|
-
/* @__PURE__ */ jsxDEV5(KeyBinding, {
|
|
1383
|
-
keys: "1/2",
|
|
1384
|
-
description: "Switch tabs"
|
|
1385
|
-
}, undefined, false, undefined, this),
|
|
1386
|
-
/* @__PURE__ */ jsxDEV5(KeyBinding, {
|
|
1387
|
-
keys: "h/l",
|
|
1388
|
-
description: "Previous/next tab"
|
|
1389
|
-
}, undefined, false, undefined, this),
|
|
1390
|
-
/* @__PURE__ */ jsxDEV5(KeyBinding, {
|
|
1391
|
-
keys: "Tab",
|
|
1392
|
-
description: "Cycle tabs"
|
|
1393
|
-
}, undefined, false, undefined, this),
|
|
1394
|
-
/* @__PURE__ */ jsxDEV5("text", {}, undefined, false, undefined, this),
|
|
1395
|
-
/* @__PURE__ */ jsxDEV5(Section, {
|
|
1396
|
-
title: "Scrolling"
|
|
1397
|
-
}, undefined, false, undefined, this),
|
|
1398
|
-
/* @__PURE__ */ jsxDEV5(KeyBinding, {
|
|
1399
|
-
keys: "j/k",
|
|
1400
|
-
description: "Scroll down/up"
|
|
1401
|
-
}, undefined, false, undefined, this),
|
|
1402
|
-
/* @__PURE__ */ jsxDEV5(KeyBinding, {
|
|
1403
|
-
keys: "g/G",
|
|
1404
|
-
description: "Top/bottom"
|
|
1405
|
-
}, undefined, false, undefined, this),
|
|
1406
|
-
/* @__PURE__ */ jsxDEV5("text", {}, undefined, false, undefined, this),
|
|
1407
|
-
/* @__PURE__ */ jsxDEV5(Section, {
|
|
1408
|
-
title: "Actions"
|
|
1409
|
-
}, undefined, false, undefined, this),
|
|
1410
|
-
/* @__PURE__ */ jsxDEV5(KeyBinding, {
|
|
1411
|
-
keys: "d",
|
|
1412
|
-
description: "Deploy all"
|
|
1413
|
-
}, undefined, false, undefined, this),
|
|
1414
|
-
/* @__PURE__ */ jsxDEV5(KeyBinding, {
|
|
1415
|
-
keys: "?",
|
|
1416
|
-
description: "Toggle help"
|
|
1417
|
-
}, undefined, false, undefined, this),
|
|
1418
|
-
/* @__PURE__ */ jsxDEV5(KeyBinding, {
|
|
1419
|
-
keys: "Esc",
|
|
1420
|
-
description: "Close help"
|
|
1421
|
-
}, undefined, false, undefined, this)
|
|
1422
|
-
]
|
|
1423
|
-
}, undefined, true, undefined, this)
|
|
1424
|
-
}, undefined, false, undefined, this);
|
|
1743
|
+
// ../../packages/api/actions/deployments.ts
|
|
1744
|
+
var init_deployments = () => {};
|
|
1745
|
+
|
|
1746
|
+
// ../../packages/api/actions/invocations.ts
|
|
1747
|
+
var init_invocations = () => {};
|
|
1748
|
+
|
|
1749
|
+
// ../../packages/api/actions/logs.ts
|
|
1750
|
+
var getSystemLogs = async (public_key, query, url) => {
|
|
1751
|
+
const path = buildUrl("/api/system/logs", { url, query });
|
|
1752
|
+
return apiGet(path, { publicKey: public_key });
|
|
1753
|
+
}, searchDeploymentLogs = async (public_key, props, url) => {
|
|
1754
|
+
const path = buildUrl("/api/logs/search", { url, query: props });
|
|
1755
|
+
return apiGet(path, { publicKey: public_key });
|
|
1425
1756
|
};
|
|
1426
|
-
var
|
|
1757
|
+
var init_logs3 = () => {};
|
|
1427
1758
|
|
|
1428
|
-
//
|
|
1429
|
-
var
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1759
|
+
// ../../packages/api/actions/assets.ts
|
|
1760
|
+
var init_assets = () => {};
|
|
1761
|
+
|
|
1762
|
+
// ../../packages/api/actions/activity.ts
|
|
1763
|
+
var init_activity = () => {};
|
|
1764
|
+
|
|
1765
|
+
// ../../packages/api/actions/index.ts
|
|
1766
|
+
var init_actions = __esm(() => {
|
|
1767
|
+
init_deployments();
|
|
1768
|
+
init_invocations();
|
|
1769
|
+
init_logs3();
|
|
1770
|
+
init_assets();
|
|
1771
|
+
init_activity();
|
|
1772
|
+
});
|
|
1773
|
+
|
|
1774
|
+
// ../../packages/api/index.ts
|
|
1775
|
+
var init_api = __esm(() => {
|
|
1776
|
+
init_types();
|
|
1777
|
+
init_actions();
|
|
1778
|
+
});
|
|
1779
|
+
|
|
1780
|
+
// src/ui/hooks/useLogs.ts
|
|
1781
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
1782
|
+
var useLogs = (options) => {
|
|
1783
|
+
const { config, pollInterval = 2000 } = options;
|
|
1784
|
+
const [systemLogs, setSystemLogs] = useState3([]);
|
|
1785
|
+
const [deploymentLogs, setDeploymentLogs] = useState3([]);
|
|
1786
|
+
const [loading, setLoading] = useState3(true);
|
|
1787
|
+
const [error, setError] = useState3(null);
|
|
1788
|
+
useEffect3(() => {
|
|
1789
|
+
const fetchLogs = async () => {
|
|
1453
1790
|
try {
|
|
1454
1791
|
setLoading(true);
|
|
1455
1792
|
setError(null);
|
|
1456
1793
|
const [deploymentLogsData, systemLogsData] = await Promise.all([
|
|
1457
|
-
searchDeploymentLogs(
|
|
1458
|
-
getSystemLogs(
|
|
1794
|
+
searchDeploymentLogs(config.key.public, { limit: 100 }, config.api),
|
|
1795
|
+
getSystemLogs(config.key.public, { limit: 100 }, config.api)
|
|
1459
1796
|
]);
|
|
1460
1797
|
setDeploymentLogs(deploymentLogsData);
|
|
1461
1798
|
setSystemLogs(systemLogsData);
|
|
1462
1799
|
} catch (err) {
|
|
1463
1800
|
setError(err instanceof Error ? err.message : String(err));
|
|
1464
|
-
console.error("Failed to fetch logs:", err);
|
|
1465
1801
|
} finally {
|
|
1466
1802
|
setLoading(false);
|
|
1467
1803
|
}
|
|
1468
1804
|
};
|
|
1469
|
-
|
|
1470
|
-
}, [
|
|
1471
|
-
|
|
1805
|
+
fetchLogs();
|
|
1806
|
+
}, [config.key.public, config.api]);
|
|
1807
|
+
useEffect3(() => {
|
|
1808
|
+
if (loading)
|
|
1809
|
+
return;
|
|
1472
1810
|
const interval = setInterval(async () => {
|
|
1473
|
-
if (loading)
|
|
1474
|
-
return;
|
|
1475
1811
|
try {
|
|
1476
1812
|
const [newDeploymentLogs, newSystemLogs] = await Promise.all([
|
|
1477
|
-
searchDeploymentLogs(
|
|
1478
|
-
getSystemLogs(
|
|
1813
|
+
searchDeploymentLogs(config.key.public, { limit: 100 }, config.api),
|
|
1814
|
+
getSystemLogs(config.key.public, { limit: 100 }, config.api)
|
|
1479
1815
|
]);
|
|
1480
1816
|
if (newDeploymentLogs.length > 0) {
|
|
1481
1817
|
setDeploymentLogs(newDeploymentLogs);
|
|
@@ -1483,59 +1819,206 @@ var App = (props) => {
|
|
|
1483
1819
|
if (newSystemLogs.length > 0) {
|
|
1484
1820
|
setSystemLogs(newSystemLogs);
|
|
1485
1821
|
}
|
|
1486
|
-
} catch
|
|
1487
|
-
|
|
1488
|
-
}
|
|
1489
|
-
}, 2000);
|
|
1822
|
+
} catch {}
|
|
1823
|
+
}, pollInterval);
|
|
1490
1824
|
return () => clearInterval(interval);
|
|
1491
|
-
}, [loading,
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1825
|
+
}, [loading, config.key.public, config.api, pollInterval]);
|
|
1826
|
+
return { systemLogs, deploymentLogs, loading, error };
|
|
1827
|
+
};
|
|
1828
|
+
var init_useLogs = __esm(() => {
|
|
1829
|
+
init_api();
|
|
1830
|
+
});
|
|
1831
|
+
|
|
1832
|
+
// src/ui/hooks/useStatusMessage.ts
|
|
1833
|
+
import { useState as useState4, useCallback, useRef } from "react";
|
|
1834
|
+
var DEFAULT_DURATION = 3000, useStatusMessage = () => {
|
|
1835
|
+
const [message, setMessage] = useState4(null);
|
|
1836
|
+
const timeoutRef = useRef(null);
|
|
1837
|
+
const clearMessage = useCallback(() => {
|
|
1838
|
+
if (timeoutRef.current) {
|
|
1839
|
+
clearTimeout(timeoutRef.current);
|
|
1840
|
+
timeoutRef.current = null;
|
|
1841
|
+
}
|
|
1842
|
+
setMessage(null);
|
|
1843
|
+
}, []);
|
|
1844
|
+
const showMessage = useCallback((type, icon, text, detail, duration = DEFAULT_DURATION) => {
|
|
1845
|
+
clearMessage();
|
|
1846
|
+
setMessage({ type, icon, text, detail });
|
|
1847
|
+
timeoutRef.current = setTimeout(clearMessage, duration);
|
|
1848
|
+
}, [clearMessage]);
|
|
1849
|
+
const showSuccess = useCallback((text, detail, duration) => {
|
|
1850
|
+
showMessage("success", "\u2713", text, detail, duration);
|
|
1851
|
+
}, [showMessage]);
|
|
1852
|
+
const showError = useCallback((text, detail, duration) => {
|
|
1853
|
+
showMessage("error", "\u2717", text, detail, duration);
|
|
1854
|
+
}, [showMessage]);
|
|
1855
|
+
const showWarning = useCallback((text, detail, duration) => {
|
|
1856
|
+
showMessage("warning", "!", text, detail, duration);
|
|
1857
|
+
}, [showMessage]);
|
|
1858
|
+
const showInfo = useCallback((text, detail, duration) => {
|
|
1859
|
+
showMessage("info", "\u25CB", text, detail, duration);
|
|
1860
|
+
}, [showMessage]);
|
|
1861
|
+
return {
|
|
1862
|
+
message,
|
|
1863
|
+
showMessage,
|
|
1864
|
+
showSuccess,
|
|
1865
|
+
showError,
|
|
1866
|
+
showWarning,
|
|
1867
|
+
showInfo,
|
|
1868
|
+
clearMessage
|
|
1869
|
+
};
|
|
1870
|
+
};
|
|
1871
|
+
var init_useStatusMessage = () => {};
|
|
1872
|
+
|
|
1873
|
+
// src/ui/hooks/useScrollControl.ts
|
|
1874
|
+
import { useState as useState5, useCallback as useCallback2 } from "react";
|
|
1875
|
+
var useScrollControl = () => {
|
|
1876
|
+
const [autoScroll, setAutoScroll] = useState5(true);
|
|
1877
|
+
const [jumpTrigger, setJumpTrigger] = useState5(0);
|
|
1878
|
+
const jumpToTop = useCallback2(() => {
|
|
1879
|
+
setAutoScroll(false);
|
|
1880
|
+
setJumpTrigger((prev) => prev + 1);
|
|
1881
|
+
}, []);
|
|
1882
|
+
const jumpToBottom = useCallback2(() => {
|
|
1883
|
+
setAutoScroll(true);
|
|
1884
|
+
setJumpTrigger((prev) => prev + 1);
|
|
1885
|
+
}, []);
|
|
1886
|
+
return {
|
|
1887
|
+
autoScroll,
|
|
1888
|
+
jumpTrigger,
|
|
1889
|
+
jumpToTop,
|
|
1890
|
+
jumpToBottom,
|
|
1891
|
+
setAutoScroll
|
|
1892
|
+
};
|
|
1893
|
+
};
|
|
1894
|
+
var init_useScrollControl = () => {};
|
|
1895
|
+
|
|
1896
|
+
// src/ui/hooks/useDeployment.ts
|
|
1897
|
+
import { useState as useState6, useCallback as useCallback3, useEffect as useEffect4 } from "react";
|
|
1898
|
+
var useDeployment = (options) => {
|
|
1899
|
+
const { onForceDeploy, onWatcherEvent } = options;
|
|
1900
|
+
const [isDeploying, setIsDeploying] = useState6(false);
|
|
1901
|
+
const [progress, setProgress] = useState6(null);
|
|
1902
|
+
const [completedResults, setCompletedResults] = useState6(null);
|
|
1903
|
+
const [watcherResults, setWatcherResults] = useState6(null);
|
|
1904
|
+
useEffect4(() => {
|
|
1905
|
+
onWatcherEvent((event) => {
|
|
1906
|
+
if (event.type === "deploy" && event.deployed?.length) {
|
|
1907
|
+
setWatcherResults(event.deployed);
|
|
1528
1908
|
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1909
|
+
});
|
|
1910
|
+
}, [onWatcherEvent]);
|
|
1911
|
+
const deploy = useCallback3(() => {
|
|
1912
|
+
if (isDeploying)
|
|
1913
|
+
return;
|
|
1914
|
+
setIsDeploying(true);
|
|
1915
|
+
onForceDeploy({
|
|
1916
|
+
onProgress: (p) => setProgress(p),
|
|
1917
|
+
onComplete: (results) => {
|
|
1918
|
+
setProgress(null);
|
|
1919
|
+
setCompletedResults(results);
|
|
1532
1920
|
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1921
|
+
}).finally(() => {
|
|
1922
|
+
setIsDeploying(false);
|
|
1923
|
+
});
|
|
1924
|
+
}, [isDeploying, onForceDeploy]);
|
|
1925
|
+
const clearCompleted = useCallback3(() => {
|
|
1926
|
+
setCompletedResults(null);
|
|
1927
|
+
}, []);
|
|
1928
|
+
const clearWatcherResults = useCallback3(() => {
|
|
1929
|
+
setWatcherResults(null);
|
|
1930
|
+
}, []);
|
|
1931
|
+
return {
|
|
1932
|
+
isDeploying,
|
|
1933
|
+
progress,
|
|
1934
|
+
completedResults,
|
|
1935
|
+
watcherResults,
|
|
1936
|
+
deploy,
|
|
1937
|
+
clearCompleted,
|
|
1938
|
+
clearWatcherResults
|
|
1939
|
+
};
|
|
1940
|
+
};
|
|
1941
|
+
var init_useDeployment = () => {};
|
|
1942
|
+
|
|
1943
|
+
// src/ui/hooks/useKeyboard.ts
|
|
1944
|
+
import { useEffect as useEffect5 } from "react";
|
|
1945
|
+
import { useRenderer } from "@opentui/react";
|
|
1946
|
+
var useKeyboard = (options) => {
|
|
1947
|
+
const {
|
|
1948
|
+
activeTab,
|
|
1949
|
+
isDeploying,
|
|
1950
|
+
isCleaning,
|
|
1951
|
+
onTabChange,
|
|
1952
|
+
onScrollTop,
|
|
1953
|
+
onScrollBottom,
|
|
1954
|
+
onToggleHelp,
|
|
1955
|
+
onCloseModal,
|
|
1956
|
+
onDeploy,
|
|
1957
|
+
onCopySuccess,
|
|
1958
|
+
onCopyError,
|
|
1959
|
+
onNoSelection,
|
|
1960
|
+
onClean,
|
|
1961
|
+
onCleanEmpty
|
|
1962
|
+
} = options;
|
|
1963
|
+
const renderer = useRenderer();
|
|
1964
|
+
useEffect5(() => {
|
|
1965
|
+
const handleKeyPress = (data) => {
|
|
1966
|
+
const key = data.toString();
|
|
1967
|
+
const action = KEYBINDINGS[key];
|
|
1968
|
+
if (!action)
|
|
1538
1969
|
return;
|
|
1970
|
+
switch (action.type) {
|
|
1971
|
+
case "NAVIGATE":
|
|
1972
|
+
onTabChange(action.tab);
|
|
1973
|
+
break;
|
|
1974
|
+
case "CYCLE_TAB":
|
|
1975
|
+
onTabChange(activeTab === "system" ? "deployment" : "system");
|
|
1976
|
+
break;
|
|
1977
|
+
case "SCROLL":
|
|
1978
|
+
if (action.to === "top") {
|
|
1979
|
+
onScrollTop();
|
|
1980
|
+
} else {
|
|
1981
|
+
onScrollBottom();
|
|
1982
|
+
}
|
|
1983
|
+
break;
|
|
1984
|
+
case "TOGGLE_HELP":
|
|
1985
|
+
onToggleHelp();
|
|
1986
|
+
break;
|
|
1987
|
+
case "CLOSE_MODAL":
|
|
1988
|
+
onCloseModal();
|
|
1989
|
+
break;
|
|
1990
|
+
case "DEPLOY":
|
|
1991
|
+
if (!isDeploying) {
|
|
1992
|
+
onDeploy();
|
|
1993
|
+
}
|
|
1994
|
+
break;
|
|
1995
|
+
case "YANK":
|
|
1996
|
+
if (!renderer.hasSelection) {
|
|
1997
|
+
onNoSelection();
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
const text = renderer.getSelection()?.getSelectedText();
|
|
2001
|
+
if (!text) {
|
|
2002
|
+
onNoSelection();
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
copyToClipboard(text).then(() => {
|
|
2006
|
+
renderer.clearSelection();
|
|
2007
|
+
onCopySuccess(text);
|
|
2008
|
+
}).catch(() => {
|
|
2009
|
+
onCopyError();
|
|
2010
|
+
});
|
|
2011
|
+
break;
|
|
2012
|
+
case "CLEAN":
|
|
2013
|
+
if (isCleaning)
|
|
2014
|
+
return;
|
|
2015
|
+
const { deleted } = cleanLocalData();
|
|
2016
|
+
if (deleted.length > 0) {
|
|
2017
|
+
onClean(deleted);
|
|
2018
|
+
} else {
|
|
2019
|
+
onCleanEmpty();
|
|
2020
|
+
}
|
|
2021
|
+
break;
|
|
1539
2022
|
}
|
|
1540
2023
|
};
|
|
1541
2024
|
process.stdin.setRawMode(true);
|
|
@@ -1544,14 +2027,101 @@ var App = (props) => {
|
|
|
1544
2027
|
process.stdin.setRawMode(false);
|
|
1545
2028
|
process.stdin.off("data", handleKeyPress);
|
|
1546
2029
|
};
|
|
1547
|
-
}, [
|
|
2030
|
+
}, [
|
|
2031
|
+
activeTab,
|
|
2032
|
+
isDeploying,
|
|
2033
|
+
isCleaning,
|
|
2034
|
+
renderer,
|
|
2035
|
+
onTabChange,
|
|
2036
|
+
onScrollTop,
|
|
2037
|
+
onScrollBottom,
|
|
2038
|
+
onToggleHelp,
|
|
2039
|
+
onCloseModal,
|
|
2040
|
+
onDeploy,
|
|
2041
|
+
onCopySuccess,
|
|
2042
|
+
onCopyError,
|
|
2043
|
+
onNoSelection,
|
|
2044
|
+
onClean,
|
|
2045
|
+
onCleanEmpty
|
|
2046
|
+
]);
|
|
2047
|
+
};
|
|
2048
|
+
var init_useKeyboard = __esm(() => {
|
|
2049
|
+
init_constants();
|
|
2050
|
+
init_utils();
|
|
2051
|
+
init_config();
|
|
2052
|
+
});
|
|
2053
|
+
|
|
2054
|
+
// src/ui/hooks/index.ts
|
|
2055
|
+
var init_hooks = __esm(() => {
|
|
2056
|
+
init_useLogs();
|
|
2057
|
+
init_useStatusMessage();
|
|
2058
|
+
init_useScrollControl();
|
|
2059
|
+
init_useDeployment();
|
|
2060
|
+
init_useKeyboard();
|
|
2061
|
+
});
|
|
2062
|
+
|
|
2063
|
+
// src/ui/App.tsx
|
|
2064
|
+
import { useState as useState7, useCallback as useCallback4 } from "react";
|
|
2065
|
+
import { jsxDEV as jsxDEV12 } from "@opentui/react/jsx-dev-runtime";
|
|
2066
|
+
var App = (props) => {
|
|
2067
|
+
const { config, binarySource, functionCount, viteRunning, onWatcherEvent, onForceDeploy, onRestartServer } = props;
|
|
2068
|
+
const [activeTab, setActiveTab] = useState7("system");
|
|
2069
|
+
const [showKeyBindings, setShowKeyBindings] = useState7(false);
|
|
2070
|
+
const [cleaningItems, setCleaningItems] = useState7(null);
|
|
2071
|
+
const { systemLogs, deploymentLogs, loading, error } = useLogs({ config });
|
|
2072
|
+
const { message, showInfo } = useStatusMessage();
|
|
2073
|
+
const { autoScroll, jumpTrigger, jumpToTop, jumpToBottom } = useScrollControl();
|
|
2074
|
+
const {
|
|
2075
|
+
isDeploying,
|
|
2076
|
+
progress: deployProgress,
|
|
2077
|
+
completedResults,
|
|
2078
|
+
watcherResults,
|
|
2079
|
+
deploy,
|
|
2080
|
+
clearCompleted,
|
|
2081
|
+
clearWatcherResults
|
|
2082
|
+
} = useDeployment({ onForceDeploy, onWatcherEvent });
|
|
2083
|
+
const handleCopySuccess = useCallback4((text) => {
|
|
2084
|
+
const preview = text.length > 30 ? text.substring(0, 30) + "..." : text;
|
|
2085
|
+
showInfo("Copied", `"${preview.replace(/\n/g, "\u21B5")}"`);
|
|
2086
|
+
}, [showInfo]);
|
|
2087
|
+
const handleCopyError = useCallback4(() => {
|
|
2088
|
+
showInfo("Failed to copy");
|
|
2089
|
+
}, [showInfo]);
|
|
2090
|
+
const handleNoSelection = useCallback4(() => {
|
|
2091
|
+
showInfo("No selection", "Select text first");
|
|
2092
|
+
}, [showInfo]);
|
|
2093
|
+
const handleClean = useCallback4((deletedItems) => {
|
|
2094
|
+
setCleaningItems(deletedItems);
|
|
2095
|
+
}, []);
|
|
2096
|
+
const handleCleanComplete = useCallback4(() => {
|
|
2097
|
+
setCleaningItems(null);
|
|
2098
|
+
}, []);
|
|
2099
|
+
const handleCleanEmpty = useCallback4(() => {
|
|
2100
|
+
showInfo("Nothing to clean");
|
|
2101
|
+
}, [showInfo]);
|
|
2102
|
+
useKeyboard({
|
|
2103
|
+
activeTab,
|
|
2104
|
+
isDeploying,
|
|
2105
|
+
isCleaning: cleaningItems !== null,
|
|
2106
|
+
onTabChange: setActiveTab,
|
|
2107
|
+
onScrollTop: jumpToTop,
|
|
2108
|
+
onScrollBottom: jumpToBottom,
|
|
2109
|
+
onToggleHelp: () => setShowKeyBindings((prev) => !prev),
|
|
2110
|
+
onCloseModal: () => setShowKeyBindings(false),
|
|
2111
|
+
onDeploy: deploy,
|
|
2112
|
+
onCopySuccess: handleCopySuccess,
|
|
2113
|
+
onCopyError: handleCopyError,
|
|
2114
|
+
onNoSelection: handleNoSelection,
|
|
2115
|
+
onClean: handleClean,
|
|
2116
|
+
onCleanEmpty: handleCleanEmpty
|
|
2117
|
+
});
|
|
1548
2118
|
if (error) {
|
|
1549
|
-
return /* @__PURE__ */
|
|
2119
|
+
return /* @__PURE__ */ jsxDEV12("box", {
|
|
1550
2120
|
flexGrow: 1,
|
|
1551
2121
|
flexDirection: "column",
|
|
1552
2122
|
padding: 1,
|
|
1553
|
-
children: /* @__PURE__ */
|
|
1554
|
-
children: /* @__PURE__ */
|
|
2123
|
+
children: /* @__PURE__ */ jsxDEV12("text", {
|
|
2124
|
+
children: /* @__PURE__ */ jsxDEV12("span", {
|
|
1555
2125
|
fg: "red",
|
|
1556
2126
|
children: [
|
|
1557
2127
|
"Error: ",
|
|
@@ -1561,50 +2131,72 @@ var App = (props) => {
|
|
|
1561
2131
|
}, undefined, false, undefined, this)
|
|
1562
2132
|
}, undefined, false, undefined, this);
|
|
1563
2133
|
}
|
|
1564
|
-
return /* @__PURE__ */
|
|
2134
|
+
return /* @__PURE__ */ jsxDEV12("box", {
|
|
1565
2135
|
flexDirection: "column",
|
|
2136
|
+
height: "100%",
|
|
1566
2137
|
children: [
|
|
1567
|
-
/* @__PURE__ */
|
|
2138
|
+
/* @__PURE__ */ jsxDEV12(Header, {
|
|
1568
2139
|
activeTab,
|
|
1569
|
-
functionCount
|
|
1570
|
-
viteRunning
|
|
2140
|
+
functionCount,
|
|
2141
|
+
viteRunning,
|
|
1571
2142
|
isDeploying,
|
|
1572
|
-
binarySource
|
|
2143
|
+
binarySource
|
|
1573
2144
|
}, undefined, false, undefined, this),
|
|
1574
|
-
/* @__PURE__ */
|
|
2145
|
+
/* @__PURE__ */ jsxDEV12("box", {
|
|
1575
2146
|
paddingTop: 1,
|
|
1576
2147
|
flexDirection: "column",
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
/* @__PURE__ */ jsxDEV6(HelpModal, {
|
|
2148
|
+
flexGrow: 1,
|
|
2149
|
+
overflow: "hidden",
|
|
2150
|
+
children: activeTab === "system" ? /* @__PURE__ */ jsxDEV12(SystemLogs, {
|
|
2151
|
+
logs: systemLogs,
|
|
2152
|
+
loading,
|
|
2153
|
+
autoScroll,
|
|
2154
|
+
jumpTrigger
|
|
2155
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV12(DeploymentLogs, {
|
|
2156
|
+
logs: deploymentLogs,
|
|
2157
|
+
loading,
|
|
2158
|
+
autoScroll,
|
|
2159
|
+
jumpTrigger
|
|
2160
|
+
}, undefined, false, undefined, this)
|
|
2161
|
+
}, undefined, false, undefined, this),
|
|
2162
|
+
/* @__PURE__ */ jsxDEV12(StatusBar, {
|
|
2163
|
+
deployProgress,
|
|
2164
|
+
deployComplete: completedResults ? { results: completedResults, onComplete: clearCompleted } : watcherResults ? { results: watcherResults, onComplete: clearWatcherResults } : undefined,
|
|
2165
|
+
cleanProgress: cleaningItems ? { items: cleaningItems, onRestartServer, onComplete: handleCleanComplete } : null,
|
|
2166
|
+
message
|
|
2167
|
+
}, undefined, false, undefined, this),
|
|
2168
|
+
/* @__PURE__ */ jsxDEV12(HelpModal, {
|
|
1599
2169
|
visible: showKeyBindings
|
|
1600
2170
|
}, undefined, false, undefined, this)
|
|
1601
2171
|
]
|
|
1602
2172
|
}, undefined, true, undefined, this);
|
|
1603
|
-
}
|
|
2173
|
+
};
|
|
2174
|
+
var init_App = __esm(() => {
|
|
2175
|
+
init_layout();
|
|
2176
|
+
init_logs2();
|
|
2177
|
+
init_modal();
|
|
2178
|
+
init_hooks();
|
|
2179
|
+
});
|
|
2180
|
+
|
|
2181
|
+
// src/ui.tsx
|
|
2182
|
+
var exports_ui = {};
|
|
2183
|
+
import { createCliRenderer } from "@opentui/core";
|
|
2184
|
+
import { createRoot } from "@opentui/react";
|
|
2185
|
+
import { jsxDEV as jsxDEV13 } from "@opentui/react/jsx-dev-runtime";
|
|
2186
|
+
var main = async () => {
|
|
1604
2187
|
let serverInfo = null;
|
|
1605
2188
|
let viteInfo = null;
|
|
1606
2189
|
let stopWatcher = null;
|
|
1607
2190
|
let isCleaningUp = false;
|
|
2191
|
+
const stopServer = () => {
|
|
2192
|
+
if (serverInfo) {
|
|
2193
|
+
const pid = serverInfo.process.pid;
|
|
2194
|
+
serverInfo = null;
|
|
2195
|
+
try {
|
|
2196
|
+
process.kill(pid, "SIGTERM");
|
|
2197
|
+
} catch {}
|
|
2198
|
+
}
|
|
2199
|
+
};
|
|
1608
2200
|
const cleanup = async (exitAfter = true) => {
|
|
1609
2201
|
if (isCleaningUp)
|
|
1610
2202
|
return;
|
|
@@ -1617,13 +2209,7 @@ var App = (props) => {
|
|
|
1617
2209
|
await stopViteServer(viteInfo);
|
|
1618
2210
|
viteInfo = null;
|
|
1619
2211
|
}
|
|
1620
|
-
|
|
1621
|
-
const pid = serverInfo.process.pid;
|
|
1622
|
-
serverInfo = null;
|
|
1623
|
-
try {
|
|
1624
|
-
process.kill(pid, "SIGTERM");
|
|
1625
|
-
} catch {}
|
|
1626
|
-
}
|
|
2212
|
+
stopServer();
|
|
1627
2213
|
if (exitAfter) {
|
|
1628
2214
|
process.exit(0);
|
|
1629
2215
|
}
|
|
@@ -1637,11 +2223,12 @@ var App = (props) => {
|
|
|
1637
2223
|
const srcPath = process.cwd() + "/src";
|
|
1638
2224
|
viteInfo = await startViteServer({
|
|
1639
2225
|
srcDir: srcPath,
|
|
1640
|
-
onLog: (
|
|
2226
|
+
onLog: () => {}
|
|
1641
2227
|
});
|
|
1642
2228
|
let watcherEventHandler = null;
|
|
1643
2229
|
let pendingEvents = [];
|
|
1644
2230
|
let functionCount = 0;
|
|
2231
|
+
let isInitialBuild = true;
|
|
1645
2232
|
const sendEvent = (event) => {
|
|
1646
2233
|
if (watcherEventHandler) {
|
|
1647
2234
|
watcherEventHandler(event);
|
|
@@ -1649,22 +2236,27 @@ var App = (props) => {
|
|
|
1649
2236
|
pendingEvents.push(event);
|
|
1650
2237
|
}
|
|
1651
2238
|
};
|
|
2239
|
+
const restartServer = async () => {
|
|
2240
|
+
stopServer();
|
|
2241
|
+
serverInfo = await startServer();
|
|
2242
|
+
};
|
|
1652
2243
|
const renderer = await createCliRenderer();
|
|
1653
2244
|
const root = createRoot(renderer);
|
|
1654
|
-
const handleForceDeploy = async () => {
|
|
2245
|
+
const handleForceDeploy = async (callbacks) => {
|
|
1655
2246
|
const deployResults = [];
|
|
1656
2247
|
await forceDeployAll(srcPath, {
|
|
2248
|
+
onStart: (filePath, completedCount, totalCount) => {
|
|
2249
|
+
callbacks.onProgress({
|
|
2250
|
+
currentFile: filePath,
|
|
2251
|
+
completedFiles: [...deployResults],
|
|
2252
|
+
totalFiles: totalCount
|
|
2253
|
+
});
|
|
2254
|
+
},
|
|
1657
2255
|
onDeploy: (filePath, success, error) => {
|
|
1658
2256
|
deployResults.push({ name: filePath, success, error });
|
|
1659
2257
|
}
|
|
1660
2258
|
});
|
|
1661
|
-
|
|
1662
|
-
sendEvent({
|
|
1663
|
-
type: "deploy",
|
|
1664
|
-
deployed: deployResults,
|
|
1665
|
-
timestamp: new Date
|
|
1666
|
-
});
|
|
1667
|
-
}
|
|
2259
|
+
callbacks.onComplete(deployResults);
|
|
1668
2260
|
};
|
|
1669
2261
|
stopWatcher = await startWatcher(srcPath, {
|
|
1670
2262
|
silent: true,
|
|
@@ -1672,6 +2264,10 @@ var App = (props) => {
|
|
|
1672
2264
|
functionCount = count;
|
|
1673
2265
|
},
|
|
1674
2266
|
onDeployBatch: (results) => {
|
|
2267
|
+
if (isInitialBuild) {
|
|
2268
|
+
isInitialBuild = false;
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
1675
2271
|
sendEvent({
|
|
1676
2272
|
type: "deploy",
|
|
1677
2273
|
deployed: results.map((r) => ({
|
|
@@ -1683,7 +2279,7 @@ var App = (props) => {
|
|
|
1683
2279
|
});
|
|
1684
2280
|
}
|
|
1685
2281
|
});
|
|
1686
|
-
root.render(/* @__PURE__ */
|
|
2282
|
+
root.render(/* @__PURE__ */ jsxDEV13(App, {
|
|
1687
2283
|
config: devConfig,
|
|
1688
2284
|
binarySource: serverInfo.binarySource,
|
|
1689
2285
|
functionCount,
|
|
@@ -1697,7 +2293,8 @@ var App = (props) => {
|
|
|
1697
2293
|
pendingEvents = [];
|
|
1698
2294
|
}
|
|
1699
2295
|
},
|
|
1700
|
-
onForceDeploy: handleForceDeploy
|
|
2296
|
+
onForceDeploy: handleForceDeploy,
|
|
2297
|
+
onRestartServer: restartServer
|
|
1701
2298
|
}, undefined, false, undefined, this));
|
|
1702
2299
|
} catch (error) {
|
|
1703
2300
|
console.error("Failed to start server:", error);
|
|
@@ -1705,16 +2302,11 @@ var App = (props) => {
|
|
|
1705
2302
|
}
|
|
1706
2303
|
};
|
|
1707
2304
|
var init_ui = __esm(() => {
|
|
1708
|
-
init_api();
|
|
1709
2305
|
init_server();
|
|
1710
2306
|
init_watcher();
|
|
1711
2307
|
init_vite();
|
|
1712
2308
|
init_config();
|
|
1713
|
-
|
|
1714
|
-
init_SystemLogsPane();
|
|
1715
|
-
init_DeployAnimation();
|
|
1716
|
-
init_Header();
|
|
1717
|
-
init_HelpModal();
|
|
2309
|
+
init_App();
|
|
1718
2310
|
main();
|
|
1719
2311
|
});
|
|
1720
2312
|
|
|
@@ -1732,8 +2324,8 @@ init_config();
|
|
|
1732
2324
|
init_bundle();
|
|
1733
2325
|
init_deploy2();
|
|
1734
2326
|
import chalk3 from "chalk";
|
|
1735
|
-
import { basename as basename4, resolve as resolve2, join as
|
|
1736
|
-
import { existsSync as
|
|
2327
|
+
import { basename as basename4, resolve as resolve2, join as join6 } from "path";
|
|
2328
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1737
2329
|
import { readdir as readdir2 } from "fs/promises";
|
|
1738
2330
|
import * as p from "@clack/prompts";
|
|
1739
2331
|
var FUNCTION_DIRS2 = ["api", "cron", "event"];
|
|
@@ -1762,25 +2354,25 @@ var selectConfig = async () => {
|
|
|
1762
2354
|
};
|
|
1763
2355
|
var findAllDeployables = async (srcPath) => {
|
|
1764
2356
|
const functions = [];
|
|
1765
|
-
const functionsPath =
|
|
2357
|
+
const functionsPath = join6(srcPath, "function");
|
|
1766
2358
|
for (const dir of FUNCTION_DIRS2) {
|
|
1767
|
-
const dirPath =
|
|
2359
|
+
const dirPath = join6(functionsPath, dir);
|
|
1768
2360
|
try {
|
|
1769
2361
|
const items = await readdir2(dirPath, { withFileTypes: true });
|
|
1770
2362
|
for (const item of items) {
|
|
1771
2363
|
if (item.isFile() && (item.name.endsWith(".ts") || item.name.endsWith(".tsx"))) {
|
|
1772
|
-
functions.push(
|
|
2364
|
+
functions.push(join6(dirPath, item.name));
|
|
1773
2365
|
}
|
|
1774
2366
|
}
|
|
1775
2367
|
} catch {}
|
|
1776
2368
|
}
|
|
1777
|
-
const reactEntry =
|
|
1778
|
-
const hasReact =
|
|
2369
|
+
const reactEntry = join6(srcPath, "index.tsx");
|
|
2370
|
+
const hasReact = existsSync6(reactEntry);
|
|
1779
2371
|
return { functions, reactEntry: hasReact ? reactEntry : null };
|
|
1780
2372
|
};
|
|
1781
2373
|
var deployAll = async (config, options) => {
|
|
1782
|
-
const srcPath =
|
|
1783
|
-
if (!
|
|
2374
|
+
const srcPath = join6(process.cwd(), "src");
|
|
2375
|
+
if (!existsSync6(srcPath)) {
|
|
1784
2376
|
console.error(chalk3.red("\u2717 No src directory found"));
|
|
1785
2377
|
process.exit(1);
|
|
1786
2378
|
}
|
|
@@ -1864,7 +2456,7 @@ var registerDeployCommand = (program) => {
|
|
|
1864
2456
|
return;
|
|
1865
2457
|
}
|
|
1866
2458
|
const filePath = resolve2(file);
|
|
1867
|
-
if (!
|
|
2459
|
+
if (!existsSync6(filePath)) {
|
|
1868
2460
|
console.error(chalk3.red(`\u2717 File not found: ${filePath}`));
|
|
1869
2461
|
process.exit(1);
|
|
1870
2462
|
}
|
|
@@ -2031,14 +2623,72 @@ var printAvailableCommands = () => {
|
|
|
2031
2623
|
console.log(chalk5.cyan(" nulljs host") + chalk5.gray(" - Set up production hosting with systemd"));
|
|
2032
2624
|
};
|
|
2033
2625
|
// src/commands/secret.ts
|
|
2034
|
-
init_api();
|
|
2035
|
-
init_config();
|
|
2036
2626
|
import { Command as Command2 } from "commander";
|
|
2037
2627
|
import chalk6 from "chalk";
|
|
2038
2628
|
import { resolve as resolve3 } from "path";
|
|
2039
2629
|
import { readFile, readdir as readdir3 } from "fs/promises";
|
|
2040
|
-
import { existsSync as
|
|
2630
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2041
2631
|
import * as p3 from "@clack/prompts";
|
|
2632
|
+
|
|
2633
|
+
// ../../packages/api/actions/secrets.ts
|
|
2634
|
+
init_http();
|
|
2635
|
+
var createSignatureHeader = async (privateKey, props) => {
|
|
2636
|
+
const timestamp = new Date().toISOString();
|
|
2637
|
+
const raw = `${props.method}-${props.path}-${timestamp}${props.body ? "-" + props.body : ""}`;
|
|
2638
|
+
const sign = await crypto.subtle.sign("Ed25519", privateKey, Buffer.from(raw));
|
|
2639
|
+
const signature = btoa(String.fromCharCode(...new Uint8Array(sign)));
|
|
2640
|
+
const header = {
|
|
2641
|
+
authorization: signature,
|
|
2642
|
+
"x-time": timestamp
|
|
2643
|
+
};
|
|
2644
|
+
if (props.body) {
|
|
2645
|
+
header["x-body"] = btoa(props.body);
|
|
2646
|
+
}
|
|
2647
|
+
return header;
|
|
2648
|
+
};
|
|
2649
|
+
var listSecrets = async (options) => {
|
|
2650
|
+
const baseUrl = options.url || "";
|
|
2651
|
+
const path = "/api/secrets";
|
|
2652
|
+
const fullPath = `${baseUrl}${path}`;
|
|
2653
|
+
const headers = await createSignatureHeader(options.privateKey, {
|
|
2654
|
+
method: "GET",
|
|
2655
|
+
path: fullPath
|
|
2656
|
+
});
|
|
2657
|
+
const result = await httpGet(path, {
|
|
2658
|
+
baseUrl,
|
|
2659
|
+
useCurl: options.useCurl,
|
|
2660
|
+
headers
|
|
2661
|
+
});
|
|
2662
|
+
if (!result.ok) {
|
|
2663
|
+
throw new Error(`Failed to list secrets (${result.status}): ${result.body}`);
|
|
2664
|
+
}
|
|
2665
|
+
return JSON.parse(result.body);
|
|
2666
|
+
};
|
|
2667
|
+
var createSecret = async (secret, options) => {
|
|
2668
|
+
const baseUrl = options.url || "";
|
|
2669
|
+
const path = "/api/secrets";
|
|
2670
|
+
const fullPath = `${baseUrl}${path}`;
|
|
2671
|
+
const body = JSON.stringify(secret);
|
|
2672
|
+
const signatureHeaders = await createSignatureHeader(options.privateKey, {
|
|
2673
|
+
method: "POST",
|
|
2674
|
+
path: fullPath,
|
|
2675
|
+
body
|
|
2676
|
+
});
|
|
2677
|
+
const result = await httpPost(path, body, {
|
|
2678
|
+
baseUrl,
|
|
2679
|
+
useCurl: options.useCurl,
|
|
2680
|
+
headers: {
|
|
2681
|
+
"Content-Type": "application/json",
|
|
2682
|
+
...signatureHeaders
|
|
2683
|
+
}
|
|
2684
|
+
});
|
|
2685
|
+
if (!result.ok) {
|
|
2686
|
+
throw new Error(`Failed to create secret (${result.status}): ${result.body}`);
|
|
2687
|
+
}
|
|
2688
|
+
};
|
|
2689
|
+
|
|
2690
|
+
// src/commands/secret.ts
|
|
2691
|
+
init_config();
|
|
2042
2692
|
var selectConfig2 = async () => {
|
|
2043
2693
|
const configList = listConfigs();
|
|
2044
2694
|
if (!configList || configList.configs.length === 0) {
|
|
@@ -2093,7 +2743,7 @@ var parseEnvFile = async (filePath) => {
|
|
|
2093
2743
|
const content = await readFile(filePath, "utf-8");
|
|
2094
2744
|
const lines = content.split(`
|
|
2095
2745
|
`);
|
|
2096
|
-
const
|
|
2746
|
+
const secrets = [];
|
|
2097
2747
|
for (const line of lines) {
|
|
2098
2748
|
const trimmed = line.trim();
|
|
2099
2749
|
if (!trimmed || trimmed.startsWith("#")) {
|
|
@@ -2109,13 +2759,13 @@ var parseEnvFile = async (filePath) => {
|
|
|
2109
2759
|
value = value.slice(1, -1);
|
|
2110
2760
|
}
|
|
2111
2761
|
if (key) {
|
|
2112
|
-
|
|
2762
|
+
secrets.push({ key, value });
|
|
2113
2763
|
}
|
|
2114
2764
|
}
|
|
2115
|
-
return
|
|
2765
|
+
return secrets;
|
|
2116
2766
|
};
|
|
2117
2767
|
var registerSecretCommand = (program) => {
|
|
2118
|
-
program.command("secret").description("Secret management").addCommand(new Command2("list").description("List all secret keys").option("-e, --env <name>", "Use a specific config environment").action(async (options) => {
|
|
2768
|
+
program.command("secret").description("Secret management").addCommand(new Command2("list").description("List all secret keys").option("-e, --env <name>", "Use a specific config environment").option("--curl", "Use curl instead of fetch (workaround for macOS local network issues)").action(async (options) => {
|
|
2119
2769
|
let config;
|
|
2120
2770
|
try {
|
|
2121
2771
|
config = options.env ? getConfig(options.env) : await selectConfig2();
|
|
@@ -2132,7 +2782,7 @@ var registerSecretCommand = (program) => {
|
|
|
2132
2782
|
try {
|
|
2133
2783
|
console.log(chalk6.gray(`Fetching secrets from ${config.api}...`));
|
|
2134
2784
|
const privateKey = await loadPrivateKey(config);
|
|
2135
|
-
const keys = await listSecrets({ privateKey, url: config.api });
|
|
2785
|
+
const keys = await listSecrets({ privateKey, url: config.api, useCurl: options.curl });
|
|
2136
2786
|
if (keys.length === 0) {
|
|
2137
2787
|
console.log(chalk6.yellow("No secrets found"));
|
|
2138
2788
|
return;
|
|
@@ -2146,7 +2796,7 @@ Secret keys:`));
|
|
|
2146
2796
|
console.error(chalk6.red("\u2717 Failed to list secrets:"), error instanceof Error ? error.message : String(error));
|
|
2147
2797
|
process.exit(1);
|
|
2148
2798
|
}
|
|
2149
|
-
})).addCommand(new Command2("create").description("Create a new secret").argument("[key]", "Secret key").argument("[value]", "Secret value").option("-e, --env <name>", "Use a specific config environment").action(async (key, value, options) => {
|
|
2799
|
+
})).addCommand(new Command2("create").description("Create a new secret").argument("[key]", "Secret key").argument("[value]", "Secret value").option("-e, --env <name>", "Use a specific config environment").option("--curl", "Use curl instead of fetch (workaround for macOS local network issues)").action(async (key, value, options) => {
|
|
2150
2800
|
const config = options?.env ? getConfig(options.env) : await selectConfig2();
|
|
2151
2801
|
if (!config) {
|
|
2152
2802
|
console.error(chalk6.red("\u2717 No configuration found."));
|
|
@@ -2192,13 +2842,13 @@ Secret keys:`));
|
|
|
2192
2842
|
await new Promise((resolve4) => setImmediate(resolve4));
|
|
2193
2843
|
try {
|
|
2194
2844
|
const privateKey = await loadPrivateKey(config);
|
|
2195
|
-
await createSecret({ key: secretKey, value: secretValue }, { privateKey, url: config.api });
|
|
2845
|
+
await createSecret({ key: secretKey, value: secretValue }, { privateKey, url: config.api, useCurl: options?.curl });
|
|
2196
2846
|
console.log(chalk6.green("\u2713 Secret created:") + ` ${chalk6.cyan(secretKey)}`);
|
|
2197
2847
|
} catch (error) {
|
|
2198
2848
|
console.error(chalk6.red("\u2717 Failed to create secret:"), error instanceof Error ? error.message : error);
|
|
2199
2849
|
process.exit(1);
|
|
2200
2850
|
}
|
|
2201
|
-
})).addCommand(new Command2("deploy").description("Deploy secrets from a .secret file").argument("[file]", "Path to .secret file").option("-e, --env <name>", "Use a specific config environment").action(async (file, options) => {
|
|
2851
|
+
})).addCommand(new Command2("deploy").description("Deploy secrets from a .secret file").argument("[file]", "Path to .secret file").option("-e, --env <name>", "Use a specific config environment").option("--curl", "Use curl instead of fetch (workaround for macOS local network issues)").action(async (file, options) => {
|
|
2202
2852
|
const config = options?.env ? getConfig(options.env) : await selectConfig2();
|
|
2203
2853
|
if (!config) {
|
|
2204
2854
|
console.error(chalk6.red("\u2717 No configuration found."));
|
|
@@ -2212,25 +2862,25 @@ Secret keys:`));
|
|
|
2212
2862
|
process.exit(1);
|
|
2213
2863
|
}
|
|
2214
2864
|
}
|
|
2215
|
-
if (!
|
|
2865
|
+
if (!existsSync7(filePath)) {
|
|
2216
2866
|
console.error(chalk6.red(`\u2717 File not found: ${filePath}`));
|
|
2217
2867
|
process.exit(1);
|
|
2218
2868
|
}
|
|
2219
2869
|
await new Promise((resolve4) => setImmediate(resolve4));
|
|
2220
2870
|
try {
|
|
2221
|
-
const
|
|
2222
|
-
if (
|
|
2871
|
+
const secrets = await parseEnvFile(filePath);
|
|
2872
|
+
if (secrets.length === 0) {
|
|
2223
2873
|
console.log(chalk6.yellow("No secrets found in file"));
|
|
2224
2874
|
return;
|
|
2225
2875
|
}
|
|
2226
2876
|
const privateKey = await loadPrivateKey(config);
|
|
2227
2877
|
let created = 0;
|
|
2228
2878
|
let failed = 0;
|
|
2229
|
-
console.log(chalk6.cyan(`Deploying ${
|
|
2879
|
+
console.log(chalk6.cyan(`Deploying ${secrets.length} secret(s) to ${config.name}...
|
|
2230
2880
|
`));
|
|
2231
|
-
for (const secret of
|
|
2881
|
+
for (const secret of secrets) {
|
|
2232
2882
|
try {
|
|
2233
|
-
await createSecret(secret, { privateKey, url: config.api });
|
|
2883
|
+
await createSecret(secret, { privateKey, url: config.api, useCurl: options?.curl });
|
|
2234
2884
|
console.log(chalk6.green("\u2713") + ` ${secret.key}`);
|
|
2235
2885
|
created++;
|
|
2236
2886
|
} catch (error) {
|
|
@@ -2248,7 +2898,7 @@ Secret keys:`));
|
|
|
2248
2898
|
console.error(chalk6.red("\u2717 Failed to deploy secrets:"), error instanceof Error ? error.message : error);
|
|
2249
2899
|
process.exit(1);
|
|
2250
2900
|
}
|
|
2251
|
-
})).addCommand(new Command2("import").description("Import secrets from a file (alias for deploy)").argument("[file]", "Path to .secret file").option("-e, --env <name>", "Use a specific config environment").action(async (file, options) => {
|
|
2901
|
+
})).addCommand(new Command2("import").description("Import secrets from a file (alias for deploy)").argument("[file]", "Path to .secret file").option("-e, --env <name>", "Use a specific config environment").option("--curl", "Use curl instead of fetch (workaround for macOS local network issues)").action(async (file, options) => {
|
|
2252
2902
|
const config = options?.env ? getConfig(options.env) : await selectConfig2();
|
|
2253
2903
|
if (!config) {
|
|
2254
2904
|
console.error(chalk6.red("\u2717 No configuration found."));
|
|
@@ -2262,25 +2912,25 @@ Secret keys:`));
|
|
|
2262
2912
|
process.exit(1);
|
|
2263
2913
|
}
|
|
2264
2914
|
}
|
|
2265
|
-
if (!
|
|
2915
|
+
if (!existsSync7(filePath)) {
|
|
2266
2916
|
console.error(chalk6.red(`\u2717 File not found: ${filePath}`));
|
|
2267
2917
|
process.exit(1);
|
|
2268
2918
|
}
|
|
2269
2919
|
await new Promise((resolve4) => setImmediate(resolve4));
|
|
2270
2920
|
try {
|
|
2271
|
-
const
|
|
2272
|
-
if (
|
|
2921
|
+
const secrets = await parseEnvFile(filePath);
|
|
2922
|
+
if (secrets.length === 0) {
|
|
2273
2923
|
console.log(chalk6.yellow("No secrets found in file"));
|
|
2274
2924
|
return;
|
|
2275
2925
|
}
|
|
2276
2926
|
const privateKey = await loadPrivateKey(config);
|
|
2277
2927
|
let created = 0;
|
|
2278
2928
|
let failed = 0;
|
|
2279
|
-
console.log(chalk6.cyan(`Importing ${
|
|
2929
|
+
console.log(chalk6.cyan(`Importing ${secrets.length} secret(s) to ${config.name}...
|
|
2280
2930
|
`));
|
|
2281
|
-
for (const secret of
|
|
2931
|
+
for (const secret of secrets) {
|
|
2282
2932
|
try {
|
|
2283
|
-
await createSecret(secret, { privateKey, url: config.api });
|
|
2933
|
+
await createSecret(secret, { privateKey, url: config.api, useCurl: options?.curl });
|
|
2284
2934
|
console.log(chalk6.green("\u2713") + ` ${secret.key}`);
|
|
2285
2935
|
created++;
|
|
2286
2936
|
} catch (error) {
|
|
@@ -2301,8 +2951,8 @@ Secret keys:`));
|
|
|
2301
2951
|
}));
|
|
2302
2952
|
};
|
|
2303
2953
|
// src/commands/host.ts
|
|
2304
|
-
import { existsSync as
|
|
2305
|
-
import { join as
|
|
2954
|
+
import { existsSync as existsSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2 } from "fs";
|
|
2955
|
+
import { join as join7 } from "path";
|
|
2306
2956
|
import { homedir } from "os";
|
|
2307
2957
|
var {$: $2 } = globalThis.Bun;
|
|
2308
2958
|
import chalk7 from "chalk";
|
|
@@ -2335,7 +2985,7 @@ var generateProductionKeys = async () => {
|
|
|
2335
2985
|
return { privateKey, publicKey };
|
|
2336
2986
|
};
|
|
2337
2987
|
var saveProductionConfig = async (cloudPath, privateKey, publicKey) => {
|
|
2338
|
-
const configPath =
|
|
2988
|
+
const configPath = join7(cloudPath, "config.json");
|
|
2339
2989
|
const config = {
|
|
2340
2990
|
key: {
|
|
2341
2991
|
private: privateKey,
|
|
@@ -2369,7 +3019,7 @@ WantedBy=multi-user.target
|
|
|
2369
3019
|
return serviceContent;
|
|
2370
3020
|
};
|
|
2371
3021
|
var ensureCloudDirectory = (cloudPath) => {
|
|
2372
|
-
if (!
|
|
3022
|
+
if (!existsSync8(cloudPath)) {
|
|
2373
3023
|
console.log(chalk7.blue("Creating cloud directory..."));
|
|
2374
3024
|
try {
|
|
2375
3025
|
mkdirSync3(cloudPath, { recursive: true });
|
|
@@ -2399,15 +3049,15 @@ var validateLinux = () => {
|
|
|
2399
3049
|
}
|
|
2400
3050
|
};
|
|
2401
3051
|
var validateServerBinary = (serverBinPath) => {
|
|
2402
|
-
if (!
|
|
3052
|
+
if (!existsSync8(serverBinPath)) {
|
|
2403
3053
|
console.log(chalk7.red("Server binary not found at:"), serverBinPath);
|
|
2404
3054
|
console.log(chalk7.yellow("Make sure you have installed the package correctly"));
|
|
2405
3055
|
process.exit(1);
|
|
2406
3056
|
}
|
|
2407
3057
|
};
|
|
2408
3058
|
var checkExistingProductionConfig = (cloudPath) => {
|
|
2409
|
-
const configPath =
|
|
2410
|
-
if (!
|
|
3059
|
+
const configPath = join7(cloudPath, "config.json");
|
|
3060
|
+
if (!existsSync8(configPath)) {
|
|
2411
3061
|
return null;
|
|
2412
3062
|
}
|
|
2413
3063
|
try {
|
|
@@ -2438,15 +3088,15 @@ var unhost = async (options = {}) => {
|
|
|
2438
3088
|
console.log(chalk7.yellow("Service was not enabled"));
|
|
2439
3089
|
}
|
|
2440
3090
|
const servicePath = "/etc/systemd/system/nulljs.service";
|
|
2441
|
-
if (
|
|
3091
|
+
if (existsSync8(servicePath)) {
|
|
2442
3092
|
console.log(chalk7.blue("Removing systemd service file..."));
|
|
2443
3093
|
await $2`sudo rm ${servicePath}`;
|
|
2444
3094
|
await $2`sudo systemctl daemon-reload`;
|
|
2445
3095
|
console.log(chalk7.green("Systemd service file removed"));
|
|
2446
3096
|
}
|
|
2447
3097
|
if (!options.keepData) {
|
|
2448
|
-
const defaultCloudPath =
|
|
2449
|
-
if (
|
|
3098
|
+
const defaultCloudPath = join7(homedir(), ".nulljs");
|
|
3099
|
+
if (existsSync8(defaultCloudPath)) {
|
|
2450
3100
|
console.log(chalk7.blue("Removing cloud directory..."));
|
|
2451
3101
|
await $2`rm -rf ${defaultCloudPath}`;
|
|
2452
3102
|
console.log(chalk7.green("Cloud directory removed"));
|
|
@@ -2470,9 +3120,9 @@ var unhost = async (options = {}) => {
|
|
|
2470
3120
|
};
|
|
2471
3121
|
var update = async () => {
|
|
2472
3122
|
validateLinux();
|
|
2473
|
-
const cloudPath =
|
|
3123
|
+
const cloudPath = join7(homedir(), ".nulljs");
|
|
2474
3124
|
const servicePath = "/etc/systemd/system/nulljs.service";
|
|
2475
|
-
if (!
|
|
3125
|
+
if (!existsSync8(servicePath)) {
|
|
2476
3126
|
console.log(chalk7.red("NullJS service is not installed."));
|
|
2477
3127
|
console.log(chalk7.gray('Run "nulljs host" to set up hosting first.'));
|
|
2478
3128
|
process.exit(1);
|
|
@@ -2503,7 +3153,7 @@ var update = async () => {
|
|
|
2503
3153
|
};
|
|
2504
3154
|
var host = async (cloudPath) => {
|
|
2505
3155
|
validateLinux();
|
|
2506
|
-
const resolvedCloudPath = cloudPath ? cloudPath.startsWith("/") ? cloudPath :
|
|
3156
|
+
const resolvedCloudPath = cloudPath ? cloudPath.startsWith("/") ? cloudPath : join7(process.cwd(), cloudPath) : join7(homedir(), ".nulljs");
|
|
2507
3157
|
console.log(chalk7.blue("Setting up NullJS production hosting..."));
|
|
2508
3158
|
console.log(chalk7.gray(` Cloud path: ${resolvedCloudPath}`));
|
|
2509
3159
|
const serverBinPath = getServerBinPath();
|