@kizenapps/cli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-4Z6S6LWP.js +6134 -0
- package/dist/chunk-4Z6S6LWP.js.map +1 -0
- package/dist/chunk-HJPLDI45.js +6134 -0
- package/dist/chunk-HJPLDI45.js.map +1 -0
- package/dist/chunk-LSHHXCC6.js +3639 -0
- package/dist/chunk-LSHHXCC6.js.map +1 -0
- package/dist/chunk-SEGVTWSK.js +44 -0
- package/dist/chunk-SEGVTWSK.js.map +1 -0
- package/dist/chunk-ZWE3DS7E.js +39 -0
- package/dist/chunk-ZWE3DS7E.js.map +1 -0
- package/dist/devtools-LZRAH4XS.js +90 -0
- package/dist/devtools-LZRAH4XS.js.map +1 -0
- package/dist/dist-C3F6UOWF.js +8472 -0
- package/dist/dist-C3F6UOWF.js.map +1 -0
- package/dist/dist-NUJOSN7W.js +8472 -0
- package/dist/dist-NUJOSN7W.js.map +1 -0
- package/dist/dist-QHDMIBVE.js +135 -0
- package/dist/dist-QHDMIBVE.js.map +1 -0
- package/dist/dist-ZIZQOSY3.js +135 -0
- package/dist/dist-ZIZQOSY3.js.map +1 -0
- package/dist/gzip-size-K37OQCQ2.js +137 -0
- package/dist/gzip-size-K37OQCQ2.js.map +1 -0
- package/dist/gzip-size-QRGIYYEQ.js +137 -0
- package/dist/gzip-size-QRGIYYEQ.js.map +1 -0
- package/dist/index.js +575 -552
- package/dist/index.js.map +1 -1
- package/dist/viewer/assets/AppDetailPage-BJrg5Wsz.js +1 -0
- package/dist/viewer/assets/{BootstrapContext-CEWgv2oI.js → BootstrapContext-D3lhzWap.js} +1 -1
- package/dist/viewer/assets/Card-CPMAuqAb.js +1 -0
- package/dist/viewer/assets/ConfigurationPage-D8I8rWjF.js +1 -0
- package/dist/viewer/assets/SandboxPage-ByixszHi.js +1 -0
- package/dist/viewer/assets/{VersionsPage-DOd2Hv8M.js → VersionsPage-BKul6bXI.js} +1 -1
- package/dist/viewer/assets/index-BoSQQwMQ.css +2 -0
- package/dist/viewer/assets/index-Bomh3C3X.js +5 -0
- package/dist/viewer/assets/useObjectLookups-Cx6A7PGj.js +14 -0
- package/dist/viewer/index.html +4 -4
- package/package.json +10 -10
- package/dist/viewer/assets/AppDetailPage-ObugMPkn.js +0 -1
- package/dist/viewer/assets/Card-BSwG5koM.js +0 -1
- package/dist/viewer/assets/ConfigurationPage-SnFNzZgJ.js +0 -1
- package/dist/viewer/assets/SandboxPage-BFVVe-i3.js +0 -1
- package/dist/viewer/assets/configStorage-Bb-A9Nvj.js +0 -14
- package/dist/viewer/assets/index-B-4D4w_w.js +0 -5
- package/dist/viewer/assets/index-D6Un_G7k.css +0 -2
package/dist/index.js
CHANGED
|
@@ -8,8 +8,8 @@ import { createElement } from "react";
|
|
|
8
8
|
import { render } from "ink";
|
|
9
9
|
|
|
10
10
|
// src/ui/BuildUI.tsx
|
|
11
|
-
import { useEffect, useState } from "react";
|
|
12
|
-
import { Box as
|
|
11
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
12
|
+
import { Box as Box3, Text as Text4, useApp } from "ink";
|
|
13
13
|
|
|
14
14
|
// src/lib/runBuild.ts
|
|
15
15
|
import { mkdir, writeFile } from "fs/promises";
|
|
@@ -88,9 +88,26 @@ async function runBuild(pluginDir, outputDir, onStep) {
|
|
|
88
88
|
})).sort((a, b) => b.version.localeCompare(a.version, void 0, { numeric: true })) : [];
|
|
89
89
|
return serializePlugin(plugin, allReleaseNotes);
|
|
90
90
|
});
|
|
91
|
-
|
|
91
|
+
const jsonStr = JSON.stringify(bundle, null, 2);
|
|
92
|
+
const bundleSize = Buffer.byteLength(jsonStr, "utf-8");
|
|
93
|
+
await writeFile(join2(outputDir, "bundle.json"), jsonStr, "utf-8");
|
|
94
|
+
return { bundleSize };
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
// src/lib/formatBytes.ts
|
|
98
|
+
function formatBytes(n) {
|
|
99
|
+
if (n < 1024) {
|
|
100
|
+
return `${String(n)} B`;
|
|
101
|
+
}
|
|
102
|
+
if (n < 1024 * 1024) {
|
|
103
|
+
return `${(n / 1024).toFixed(1)} KB`;
|
|
104
|
+
}
|
|
105
|
+
return `${(n / (1024 * 1024)).toFixed(2)} MB`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/ui/AppHeader.tsx
|
|
109
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
110
|
+
|
|
94
111
|
// src/ui/Logo.tsx
|
|
95
112
|
import { Box, Text } from "ink";
|
|
96
113
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -116,8 +133,20 @@ var LOGO_LINES = [
|
|
|
116
133
|
];
|
|
117
134
|
var Logo = () => /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: LOGO_LINES.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "cyan", children: line }, i)) });
|
|
118
135
|
|
|
119
|
-
// src/ui/
|
|
136
|
+
// src/ui/AppHeader.tsx
|
|
120
137
|
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
138
|
+
var AppHeader = ({ marginBottom = 0 }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx2(Logo, {}),
|
|
140
|
+
/* @__PURE__ */ jsxs(Box2, { flexDirection: "column", marginTop: 1, marginBottom, children: [
|
|
141
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "Kizen App Builder" }),
|
|
142
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2500".repeat(24) })
|
|
143
|
+
] })
|
|
144
|
+
] });
|
|
145
|
+
|
|
146
|
+
// src/ui/Spinner.tsx
|
|
147
|
+
import { useEffect, useState } from "react";
|
|
148
|
+
import { Text as Text3 } from "ink";
|
|
149
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
121
150
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
122
151
|
var Spinner = () => {
|
|
123
152
|
const [frame, setFrame] = useState(0);
|
|
@@ -129,8 +158,11 @@ var Spinner = () => {
|
|
|
129
158
|
clearInterval(id);
|
|
130
159
|
};
|
|
131
160
|
}, []);
|
|
132
|
-
return /* @__PURE__ */
|
|
161
|
+
return /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: SPINNER_FRAMES[frame] ?? "\u280B" });
|
|
133
162
|
};
|
|
163
|
+
|
|
164
|
+
// src/ui/BuildUI.tsx
|
|
165
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
134
166
|
var STEPS = [
|
|
135
167
|
"creating-dir",
|
|
136
168
|
"reading-files",
|
|
@@ -147,10 +179,12 @@ var STEP_LABELS = {
|
|
|
147
179
|
};
|
|
148
180
|
var BuildUI = ({ outputDir, pluginDir }) => {
|
|
149
181
|
const app = useApp();
|
|
150
|
-
const [step, setStep] =
|
|
151
|
-
const [errorMessage, setErrorMessage] =
|
|
152
|
-
|
|
153
|
-
|
|
182
|
+
const [step, setStep] = useState2("creating-dir");
|
|
183
|
+
const [errorMessage, setErrorMessage] = useState2(null);
|
|
184
|
+
const [bundleSize, setBundleSize] = useState2(null);
|
|
185
|
+
useEffect2(() => {
|
|
186
|
+
void runBuild(pluginDir, outputDir, setStep).then(({ bundleSize: size }) => {
|
|
187
|
+
setBundleSize(size);
|
|
154
188
|
setStep("done");
|
|
155
189
|
app.exit();
|
|
156
190
|
}).catch((err) => {
|
|
@@ -163,32 +197,32 @@ var BuildUI = ({ outputDir, pluginDir }) => {
|
|
|
163
197
|
const currentIndex = STEPS.indexOf(step);
|
|
164
198
|
const isError = step === "error";
|
|
165
199
|
const isDone = step === "done";
|
|
166
|
-
return /* @__PURE__ */
|
|
167
|
-
/* @__PURE__ */
|
|
168
|
-
/* @__PURE__ */
|
|
169
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "Kizen App Builder" }),
|
|
170
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2500".repeat(24) })
|
|
171
|
-
] }),
|
|
172
|
-
/* @__PURE__ */ jsxs(Box2, { flexDirection: "column", gap: 0, children: [
|
|
200
|
+
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingY: 1, paddingX: 2, children: [
|
|
201
|
+
/* @__PURE__ */ jsx4(AppHeader, { marginBottom: 1 }),
|
|
202
|
+
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
|
|
173
203
|
STEPS.map((s, i) => {
|
|
174
204
|
const isActive = step === s;
|
|
175
205
|
const isStepDone = isDone || currentIndex > i;
|
|
176
206
|
const isFailed = isError && i === currentIndex;
|
|
177
|
-
return /* @__PURE__ */
|
|
207
|
+
return /* @__PURE__ */ jsx4(Box3, { gap: 1, children: isFailed ? /* @__PURE__ */ jsxs2(Text4, { color: "red", children: [
|
|
178
208
|
"\u2717 ",
|
|
179
209
|
STEP_LABELS[s]
|
|
180
|
-
] }) : isStepDone ? /* @__PURE__ */
|
|
210
|
+
] }) : isStepDone ? /* @__PURE__ */ jsxs2(Text4, { color: "green", children: [
|
|
181
211
|
"\u2713 ",
|
|
182
212
|
STEP_LABELS[s]
|
|
183
|
-
] }) : isActive ? /* @__PURE__ */
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
-
/* @__PURE__ */
|
|
186
|
-
] }) : /* @__PURE__ */
|
|
213
|
+
] }) : isActive ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
214
|
+
/* @__PURE__ */ jsx4(Spinner, {}),
|
|
215
|
+
/* @__PURE__ */ jsx4(Text4, { children: STEP_LABELS[s] })
|
|
216
|
+
] }) : /* @__PURE__ */ jsxs2(Text4, { dimColor: true, children: [
|
|
187
217
|
" ",
|
|
188
218
|
STEP_LABELS[s]
|
|
189
219
|
] }) }, s);
|
|
190
220
|
}),
|
|
191
|
-
|
|
221
|
+
isDone && bundleSize !== null && /* @__PURE__ */ jsx4(Box3, { marginTop: 1, gap: 1, children: /* @__PURE__ */ jsxs2(Text4, { color: "green", children: [
|
|
222
|
+
"\u2713 bundle.json \u2014 ",
|
|
223
|
+
formatBytes(bundleSize)
|
|
224
|
+
] }) }),
|
|
225
|
+
errorMessage !== null && /* @__PURE__ */ jsx4(Box3, { marginTop: 1, borderStyle: "single", borderColor: "red", paddingX: 1, children: /* @__PURE__ */ jsx4(Text4, { color: "red", children: errorMessage }) })
|
|
192
226
|
] })
|
|
193
227
|
] });
|
|
194
228
|
};
|
|
@@ -227,22 +261,350 @@ import { createElement as createElement2 } from "react";
|
|
|
227
261
|
import { render as render2 } from "ink";
|
|
228
262
|
|
|
229
263
|
// src/ui/DevUI.tsx
|
|
230
|
-
import { useCallback as
|
|
231
|
-
import { Box as
|
|
232
|
-
import {
|
|
233
|
-
import { access, readFile as readFile5 } from "fs/promises";
|
|
264
|
+
import { useCallback as useCallback3, useEffect as useEffect4, useRef, useState as useState5 } from "react";
|
|
265
|
+
import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
|
|
266
|
+
import { watch } from "fs";
|
|
234
267
|
import { createServer } from "http";
|
|
235
|
-
import {
|
|
236
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
268
|
+
import { join as join8 } from "path";
|
|
237
269
|
import { WebSocketServer } from "ws";
|
|
238
270
|
|
|
271
|
+
// src/server/requestHandler.ts
|
|
272
|
+
import { createReadStream } from "fs";
|
|
273
|
+
import { access, readFile as readFile4 } from "fs/promises";
|
|
274
|
+
import { dirname as dirname2, extname, join as join6 } from "path";
|
|
275
|
+
import { fileURLToPath } from "url";
|
|
276
|
+
|
|
277
|
+
// src/lib/proxyCache.ts
|
|
278
|
+
function createProxyCache(options = {}) {
|
|
279
|
+
const cache = /* @__PURE__ */ new Map();
|
|
280
|
+
const { onChange } = options;
|
|
281
|
+
const emitChange = () => {
|
|
282
|
+
onChange?.(cache.size);
|
|
283
|
+
};
|
|
284
|
+
return {
|
|
285
|
+
get(key, fetcher) {
|
|
286
|
+
const existing = cache.get(key);
|
|
287
|
+
if (existing !== void 0) {
|
|
288
|
+
return existing.then((response) => ({ response, fromCache: true }));
|
|
289
|
+
}
|
|
290
|
+
const pending = fetcher().then(async (response) => {
|
|
291
|
+
const headers = Object.fromEntries(response.headers);
|
|
292
|
+
delete headers["content-encoding"];
|
|
293
|
+
delete headers["content-length"];
|
|
294
|
+
return {
|
|
295
|
+
status: response.status,
|
|
296
|
+
headers,
|
|
297
|
+
body: Buffer.from(await response.arrayBuffer())
|
|
298
|
+
};
|
|
299
|
+
}).catch((error) => {
|
|
300
|
+
cache.delete(key);
|
|
301
|
+
emitChange();
|
|
302
|
+
throw error;
|
|
303
|
+
});
|
|
304
|
+
cache.set(key, pending);
|
|
305
|
+
emitChange();
|
|
306
|
+
return pending.then((response) => {
|
|
307
|
+
if (response.status >= 400) {
|
|
308
|
+
cache.delete(key);
|
|
309
|
+
emitChange();
|
|
310
|
+
}
|
|
311
|
+
return { response, fromCache: false };
|
|
312
|
+
});
|
|
313
|
+
},
|
|
314
|
+
clear() {
|
|
315
|
+
cache.clear();
|
|
316
|
+
emitChange();
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/lib/constants.ts
|
|
322
|
+
var LOG_LIMIT = 50;
|
|
323
|
+
var LOG_DISPLAY = 8;
|
|
324
|
+
var FILE_WATCH_DEBOUNCE_MS = 150;
|
|
325
|
+
var MIME_TYPES = {
|
|
326
|
+
".html": "text/html; charset=utf-8",
|
|
327
|
+
".js": "application/javascript; charset=utf-8",
|
|
328
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
329
|
+
".css": "text/css; charset=utf-8",
|
|
330
|
+
".png": "image/png",
|
|
331
|
+
".jpg": "image/jpeg",
|
|
332
|
+
".svg": "image/svg+xml",
|
|
333
|
+
".ico": "image/x-icon",
|
|
334
|
+
".json": "application/json; charset=utf-8",
|
|
335
|
+
".woff": "font/woff",
|
|
336
|
+
".woff2": "font/woff2",
|
|
337
|
+
".ttf": "font/ttf",
|
|
338
|
+
".map": "application/json"
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// src/lib/credentials.ts
|
|
342
|
+
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir2, writeFile as writeFile2 } from "fs/promises";
|
|
343
|
+
import { homedir } from "os";
|
|
344
|
+
import { dirname, join as join4 } from "path";
|
|
345
|
+
var ENVIRONMENTS = ["go", "fmo", "staging", "integration", "test1"];
|
|
346
|
+
var GLOBAL_CREDENTIALS_DIR = join4(homedir(), ".kizenappbuilder");
|
|
347
|
+
var GLOBAL_CREDENTIALS_PATH = join4(GLOBAL_CREDENTIALS_DIR, "credentials.json");
|
|
348
|
+
var DEFAULT_PROFILE_NAME = "credentials";
|
|
349
|
+
function isValidEnvironment(value) {
|
|
350
|
+
return ENVIRONMENTS.includes(value);
|
|
351
|
+
}
|
|
352
|
+
function parseCredentials(raw) {
|
|
353
|
+
if (typeof raw !== "object" || raw === null) {
|
|
354
|
+
throw new Error("Credentials must be a JSON object");
|
|
355
|
+
}
|
|
356
|
+
const obj = raw;
|
|
357
|
+
const env = obj.environment;
|
|
358
|
+
return {
|
|
359
|
+
apiKey: typeof obj.apiKey === "string" ? obj.apiKey : "",
|
|
360
|
+
userId: typeof obj.userId === "string" ? obj.userId : "",
|
|
361
|
+
businessId: typeof obj.businessId === "string" ? obj.businessId : "",
|
|
362
|
+
environment: isValidEnvironment(env) ? env : "go"
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
async function loadCredentialsFromFile(filePath) {
|
|
366
|
+
const content = await readFile2(filePath, "utf-8");
|
|
367
|
+
return parseCredentials(JSON.parse(content));
|
|
368
|
+
}
|
|
369
|
+
async function loadGlobalCredentials() {
|
|
370
|
+
try {
|
|
371
|
+
return await loadCredentialsFromFile(GLOBAL_CREDENTIALS_PATH);
|
|
372
|
+
} catch {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async function saveGlobalCredentials(credentials) {
|
|
377
|
+
await mkdir2(dirname(GLOBAL_CREDENTIALS_PATH), { recursive: true });
|
|
378
|
+
await writeFile2(GLOBAL_CREDENTIALS_PATH, JSON.stringify(credentials, null, 2), "utf-8");
|
|
379
|
+
}
|
|
380
|
+
function getProfilePath(name) {
|
|
381
|
+
return join4(GLOBAL_CREDENTIALS_DIR, `${name}.json`);
|
|
382
|
+
}
|
|
383
|
+
async function listCredentialProfiles() {
|
|
384
|
+
const profiles = [
|
|
385
|
+
{ name: DEFAULT_PROFILE_NAME, path: GLOBAL_CREDENTIALS_PATH, isDefault: true }
|
|
386
|
+
];
|
|
387
|
+
try {
|
|
388
|
+
const entries = await readdir2(GLOBAL_CREDENTIALS_DIR);
|
|
389
|
+
for (const entry of entries) {
|
|
390
|
+
if (!entry.endsWith(".json")) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const name = entry.slice(0, -5);
|
|
394
|
+
if (name === DEFAULT_PROFILE_NAME) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
profiles.push({ name, path: join4(GLOBAL_CREDENTIALS_DIR, entry), isDefault: false });
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
return profiles;
|
|
402
|
+
}
|
|
403
|
+
async function saveCredentialProfile(name, credentials) {
|
|
404
|
+
if (name === DEFAULT_PROFILE_NAME) {
|
|
405
|
+
await saveGlobalCredentials(credentials);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
await mkdir2(GLOBAL_CREDENTIALS_DIR, { recursive: true });
|
|
409
|
+
await writeFile2(getProfilePath(name), JSON.stringify(credentials, null, 2), "utf-8");
|
|
410
|
+
}
|
|
411
|
+
async function loadCredentialProfile(name) {
|
|
412
|
+
try {
|
|
413
|
+
return await loadCredentialsFromFile(getProfilePath(name));
|
|
414
|
+
} catch {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/lib/config.ts
|
|
420
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
421
|
+
import { join as join5 } from "path";
|
|
422
|
+
async function loadConfig(outputDir) {
|
|
423
|
+
try {
|
|
424
|
+
const content = await readFile3(join5(outputDir, "config.json"), "utf-8");
|
|
425
|
+
return JSON.parse(content);
|
|
426
|
+
} catch {
|
|
427
|
+
return {};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async function saveConfig(outputDir, config) {
|
|
431
|
+
await mkdir3(outputDir, { recursive: true });
|
|
432
|
+
await writeFile3(join5(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// src/server/requestHandler.ts
|
|
436
|
+
function proxyLogEntryToString(entry) {
|
|
437
|
+
if (entry.kind === "info") {
|
|
438
|
+
return entry.message;
|
|
439
|
+
}
|
|
440
|
+
const cache = entry.fromCache ? " [cache]" : "";
|
|
441
|
+
return `${entry.method} ${entry.url} \u2192 ${String(entry.status)}${cache}`;
|
|
442
|
+
}
|
|
443
|
+
function getViewerPath() {
|
|
444
|
+
const filename = fileURLToPath(import.meta.url);
|
|
445
|
+
return join6(dirname2(filename), "viewer");
|
|
446
|
+
}
|
|
447
|
+
async function fileExists(filePath) {
|
|
448
|
+
try {
|
|
449
|
+
await access(filePath);
|
|
450
|
+
return true;
|
|
451
|
+
} catch {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast, onProxyCacheChange) {
|
|
456
|
+
const proxyCache = createProxyCache({ onChange: onProxyCacheChange });
|
|
457
|
+
return (req, res) => {
|
|
458
|
+
void (async () => {
|
|
459
|
+
const url = req.url ?? "/";
|
|
460
|
+
try {
|
|
461
|
+
createServerLog(`Received request: ${url}`);
|
|
462
|
+
if (url === "/api/credentials") {
|
|
463
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
464
|
+
res.end(credentialsRef.current !== null ? JSON.stringify(credentialsRef.current) : "{}");
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (url === "/api/credential-profiles" && req.method === "GET") {
|
|
468
|
+
const profiles = await listCredentialProfiles();
|
|
469
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
470
|
+
res.end(JSON.stringify({ profiles, active: activeProfileRef.current }));
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (url === "/api/credentials/switch" && req.method === "POST") {
|
|
474
|
+
const chunks = [];
|
|
475
|
+
for await (const chunk of req) {
|
|
476
|
+
chunks.push(chunk);
|
|
477
|
+
}
|
|
478
|
+
const body = JSON.parse(Buffer.concat(chunks).toString("utf-8") || "{}");
|
|
479
|
+
const profileName = body.profile;
|
|
480
|
+
const creds = profileName === void 0 || profileName === "" ? await loadGlobalCredentials() : await loadCredentialProfile(profileName);
|
|
481
|
+
if (creds !== null) {
|
|
482
|
+
credentialsRef.current = creds;
|
|
483
|
+
activeProfileRef.current = profileName ?? void 0;
|
|
484
|
+
const active = activeProfileRef.current;
|
|
485
|
+
await saveConfig(outputDir, {
|
|
486
|
+
credentialMode: "global",
|
|
487
|
+
...active !== void 0 && { activeCredentialProfile: active }
|
|
488
|
+
});
|
|
489
|
+
broadcast({ type: "credentials-updated", credentials: creds });
|
|
490
|
+
}
|
|
491
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
492
|
+
res.end(JSON.stringify({ ok: creds !== null }));
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if (url === "/api/bundle") {
|
|
496
|
+
const bundlePath = join6(process.cwd(), ".kizenapp", "bundle.json");
|
|
497
|
+
try {
|
|
498
|
+
const content = await readFile4(bundlePath, "utf-8");
|
|
499
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
500
|
+
res.end(content);
|
|
501
|
+
} catch {
|
|
502
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
503
|
+
res.end("{}");
|
|
504
|
+
}
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (url === "/api/proxy-cache/clear") {
|
|
508
|
+
proxyCache.clear();
|
|
509
|
+
createProxyLog({ kind: "info", message: "Proxy cache cleared" });
|
|
510
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
511
|
+
res.end('{"ok":true}');
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (url.startsWith("/api/proxy")) {
|
|
515
|
+
const proxyTarget = req.headers["x-proxy-target"];
|
|
516
|
+
if (typeof proxyTarget !== "string") {
|
|
517
|
+
res.writeHead(400);
|
|
518
|
+
res.end("Missing x-proxy-target header");
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const upstreamPath = url.slice("/api/proxy".length) || "/";
|
|
522
|
+
const upstreamUrl = `${proxyTarget}${upstreamPath}`;
|
|
523
|
+
const method = req.method ?? "GET";
|
|
524
|
+
const chunks = [];
|
|
525
|
+
for await (const chunk of req) {
|
|
526
|
+
chunks.push(chunk);
|
|
527
|
+
}
|
|
528
|
+
const { host, "x-proxy-target": _drop, ...forwardHeaders } = req.headers;
|
|
529
|
+
if (method === "GET") {
|
|
530
|
+
const cacheKey = `GET::${upstreamUrl}`;
|
|
531
|
+
const { response: cached, fromCache } = await proxyCache.get(
|
|
532
|
+
cacheKey,
|
|
533
|
+
() => fetch(upstreamUrl, {
|
|
534
|
+
method: "GET",
|
|
535
|
+
headers: forwardHeaders
|
|
536
|
+
})
|
|
537
|
+
);
|
|
538
|
+
createProxyLog({
|
|
539
|
+
kind: "request",
|
|
540
|
+
method: "GET",
|
|
541
|
+
status: cached.status,
|
|
542
|
+
fromCache,
|
|
543
|
+
url: upstreamPath
|
|
544
|
+
});
|
|
545
|
+
res.writeHead(cached.status, cached.headers);
|
|
546
|
+
res.end(cached.body);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const body = chunks.length > 0 ? Buffer.concat(chunks) : void 0;
|
|
550
|
+
const resolvedBody = body && body.length > 0 ? body : void 0;
|
|
551
|
+
const upstream = await fetch(upstreamUrl, {
|
|
552
|
+
method,
|
|
553
|
+
headers: forwardHeaders,
|
|
554
|
+
...resolvedBody !== void 0 && { body: resolvedBody }
|
|
555
|
+
});
|
|
556
|
+
createProxyLog({
|
|
557
|
+
kind: "request",
|
|
558
|
+
method,
|
|
559
|
+
status: upstream.status,
|
|
560
|
+
fromCache: false,
|
|
561
|
+
url: upstreamPath
|
|
562
|
+
});
|
|
563
|
+
const responseHeaders = Object.fromEntries(upstream.headers);
|
|
564
|
+
delete responseHeaders["content-encoding"];
|
|
565
|
+
delete responseHeaders["content-length"];
|
|
566
|
+
res.writeHead(upstream.status, responseHeaders);
|
|
567
|
+
res.end(Buffer.from(await upstream.arrayBuffer()));
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const rawPath = url === "/" ? "/index.html" : url;
|
|
571
|
+
const filePath = join6(viewerPath, rawPath);
|
|
572
|
+
const resolvedPath = await fileExists(filePath) ? filePath : join6(viewerPath, "index.html");
|
|
573
|
+
const ext = extname(resolvedPath);
|
|
574
|
+
const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
575
|
+
res.writeHead(200, { "Content-Type": mimeType });
|
|
576
|
+
createReadStream(resolvedPath).pipe(res);
|
|
577
|
+
} catch (err) {
|
|
578
|
+
if (url.startsWith("/api/proxy")) {
|
|
579
|
+
createProxyLog({
|
|
580
|
+
kind: "request",
|
|
581
|
+
method: req.method ?? "GET",
|
|
582
|
+
status: 502,
|
|
583
|
+
fromCache: false,
|
|
584
|
+
url: url.slice("/api/proxy".length) || "/"
|
|
585
|
+
});
|
|
586
|
+
} else {
|
|
587
|
+
createProxyLog({
|
|
588
|
+
kind: "info",
|
|
589
|
+
message: `Error handling ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
if (!res.headersSent) {
|
|
593
|
+
res.writeHead(502);
|
|
594
|
+
res.end("Bad Gateway");
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
})();
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
|
|
239
601
|
// src/chrome/launcher.ts
|
|
240
602
|
import { launch } from "chrome-launcher";
|
|
241
603
|
import { createRequire } from "module";
|
|
242
604
|
import { mkdirSync } from "fs";
|
|
243
|
-
import { readFile as
|
|
244
|
-
import { dirname, join as
|
|
245
|
-
import { fileURLToPath } from "url";
|
|
605
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
606
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
607
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
246
608
|
var CDP = createRequire(import.meta.url)("chrome-remote-interface");
|
|
247
609
|
var CSP_HEADERS = /* @__PURE__ */ new Set([
|
|
248
610
|
"content-security-policy",
|
|
@@ -320,8 +682,8 @@ async function serializeArgAsync(client, arg) {
|
|
|
320
682
|
}
|
|
321
683
|
async function installDockIcon(client) {
|
|
322
684
|
try {
|
|
323
|
-
const iconPath =
|
|
324
|
-
const icon = await
|
|
685
|
+
const iconPath = join7(dirname3(fileURLToPath2(import.meta.url)), "viewer", "icon.png");
|
|
686
|
+
const icon = await readFile5(iconPath);
|
|
325
687
|
await client.Browser.setDockTile({ image: icon.toString("base64") });
|
|
326
688
|
} catch {
|
|
327
689
|
}
|
|
@@ -463,190 +825,65 @@ async function launchChromeViewer(opts) {
|
|
|
463
825
|
}
|
|
464
826
|
});
|
|
465
827
|
c.on("Runtime.consoleAPICalled", (rawParams) => {
|
|
466
|
-
void (async () => {
|
|
467
|
-
const { type, args } = rawParams;
|
|
468
|
-
if (args[0]?.type === "string" && String(args[0].value).includes("Chrome Security")) {
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
const level = type === "warning" ? "warn" : ["log", "warn", "error", "info"].includes(type) ? type : "log";
|
|
472
|
-
const serialized = await Promise.all(args.map((arg) => serializeArgAsync(c, arg)));
|
|
473
|
-
opts.broadcast({ type: "console-message", level, args: serialized });
|
|
474
|
-
})().catch(() => {
|
|
475
|
-
});
|
|
476
|
-
});
|
|
477
|
-
const kill = () => {
|
|
478
|
-
clearInterval(cookieTimer);
|
|
479
|
-
try {
|
|
480
|
-
void c.close();
|
|
481
|
-
} catch {
|
|
482
|
-
}
|
|
483
|
-
chrome.kill();
|
|
484
|
-
};
|
|
485
|
-
return { kill };
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// src/lib/proxyCache.ts
|
|
489
|
-
function createProxyCache(options = {}) {
|
|
490
|
-
const cache = /* @__PURE__ */ new Map();
|
|
491
|
-
const { onChange } = options;
|
|
492
|
-
const emitChange = () => {
|
|
493
|
-
onChange?.(cache.size);
|
|
494
|
-
};
|
|
495
|
-
return {
|
|
496
|
-
get(key, fetcher) {
|
|
497
|
-
const existing = cache.get(key);
|
|
498
|
-
if (existing !== void 0) {
|
|
499
|
-
return existing.then((response) => ({ response, fromCache: true }));
|
|
500
|
-
}
|
|
501
|
-
const pending = fetcher().then(async (response) => {
|
|
502
|
-
const headers = Object.fromEntries(response.headers);
|
|
503
|
-
delete headers["content-encoding"];
|
|
504
|
-
delete headers["content-length"];
|
|
505
|
-
return {
|
|
506
|
-
status: response.status,
|
|
507
|
-
headers,
|
|
508
|
-
body: Buffer.from(await response.arrayBuffer())
|
|
509
|
-
};
|
|
510
|
-
}).catch((error) => {
|
|
511
|
-
cache.delete(key);
|
|
512
|
-
emitChange();
|
|
513
|
-
throw error;
|
|
514
|
-
});
|
|
515
|
-
cache.set(key, pending);
|
|
516
|
-
emitChange();
|
|
517
|
-
return pending.then((response) => {
|
|
518
|
-
if (response.status >= 400) {
|
|
519
|
-
cache.delete(key);
|
|
520
|
-
emitChange();
|
|
521
|
-
}
|
|
522
|
-
return { response, fromCache: false };
|
|
523
|
-
});
|
|
524
|
-
},
|
|
525
|
-
clear() {
|
|
526
|
-
cache.clear();
|
|
527
|
-
emitChange();
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// src/lib/credentials.ts
|
|
533
|
-
import { mkdir as mkdir2, readFile as readFile3, readdir as readdir2, writeFile as writeFile2 } from "fs/promises";
|
|
534
|
-
import { homedir } from "os";
|
|
535
|
-
import { dirname as dirname2, join as join5 } from "path";
|
|
536
|
-
var ENVIRONMENTS = ["go", "fmo", "staging", "integration", "test1"];
|
|
537
|
-
var GLOBAL_CREDENTIALS_DIR = join5(homedir(), ".kizenappbuilder");
|
|
538
|
-
var GLOBAL_CREDENTIALS_PATH = join5(GLOBAL_CREDENTIALS_DIR, "credentials.json");
|
|
539
|
-
var DEFAULT_PROFILE_NAME = "credentials";
|
|
540
|
-
function isValidEnvironment(value) {
|
|
541
|
-
return ENVIRONMENTS.includes(value);
|
|
542
|
-
}
|
|
543
|
-
function parseCredentials(raw) {
|
|
544
|
-
if (typeof raw !== "object" || raw === null) {
|
|
545
|
-
throw new Error("Credentials must be a JSON object");
|
|
546
|
-
}
|
|
547
|
-
const obj = raw;
|
|
548
|
-
const env = obj.environment;
|
|
549
|
-
return {
|
|
550
|
-
apiKey: typeof obj.apiKey === "string" ? obj.apiKey : "",
|
|
551
|
-
userId: typeof obj.userId === "string" ? obj.userId : "",
|
|
552
|
-
businessId: typeof obj.businessId === "string" ? obj.businessId : "",
|
|
553
|
-
environment: isValidEnvironment(env) ? env : "go"
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
async function loadCredentialsFromFile(filePath) {
|
|
557
|
-
const content = await readFile3(filePath, "utf-8");
|
|
558
|
-
return parseCredentials(JSON.parse(content));
|
|
559
|
-
}
|
|
560
|
-
async function loadGlobalCredentials() {
|
|
561
|
-
try {
|
|
562
|
-
return await loadCredentialsFromFile(GLOBAL_CREDENTIALS_PATH);
|
|
563
|
-
} catch {
|
|
564
|
-
return null;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
async function saveGlobalCredentials(credentials) {
|
|
568
|
-
await mkdir2(dirname2(GLOBAL_CREDENTIALS_PATH), { recursive: true });
|
|
569
|
-
await writeFile2(GLOBAL_CREDENTIALS_PATH, JSON.stringify(credentials, null, 2), "utf-8");
|
|
570
|
-
}
|
|
571
|
-
function getProfilePath(name) {
|
|
572
|
-
return join5(GLOBAL_CREDENTIALS_DIR, `${name}.json`);
|
|
573
|
-
}
|
|
574
|
-
async function listCredentialProfiles() {
|
|
575
|
-
const profiles = [
|
|
576
|
-
{ name: DEFAULT_PROFILE_NAME, path: GLOBAL_CREDENTIALS_PATH, isDefault: true }
|
|
577
|
-
];
|
|
578
|
-
try {
|
|
579
|
-
const entries = await readdir2(GLOBAL_CREDENTIALS_DIR);
|
|
580
|
-
for (const entry of entries) {
|
|
581
|
-
if (!entry.endsWith(".json")) {
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
584
|
-
const name = entry.slice(0, -5);
|
|
585
|
-
if (name === DEFAULT_PROFILE_NAME) {
|
|
586
|
-
continue;
|
|
828
|
+
void (async () => {
|
|
829
|
+
const { type, args } = rawParams;
|
|
830
|
+
if (args[0]?.type === "string" && String(args[0].value).includes("Chrome Security")) {
|
|
831
|
+
return;
|
|
587
832
|
}
|
|
588
|
-
|
|
833
|
+
const level = type === "warning" ? "warn" : ["log", "warn", "error", "info"].includes(type) ? type : "log";
|
|
834
|
+
const serialized = await Promise.all(args.map((arg) => serializeArgAsync(c, arg)));
|
|
835
|
+
opts.broadcast({ type: "console-message", level, args: serialized });
|
|
836
|
+
})().catch(() => {
|
|
837
|
+
});
|
|
838
|
+
});
|
|
839
|
+
const kill = () => {
|
|
840
|
+
clearInterval(cookieTimer);
|
|
841
|
+
try {
|
|
842
|
+
void c.close();
|
|
843
|
+
} catch {
|
|
589
844
|
}
|
|
590
|
-
|
|
591
|
-
}
|
|
592
|
-
return
|
|
593
|
-
}
|
|
594
|
-
async function saveCredentialProfile(name, credentials) {
|
|
595
|
-
if (name === DEFAULT_PROFILE_NAME) {
|
|
596
|
-
await saveGlobalCredentials(credentials);
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
await mkdir2(GLOBAL_CREDENTIALS_DIR, { recursive: true });
|
|
600
|
-
await writeFile2(getProfilePath(name), JSON.stringify(credentials, null, 2), "utf-8");
|
|
601
|
-
}
|
|
602
|
-
async function loadCredentialProfile(name) {
|
|
603
|
-
try {
|
|
604
|
-
return await loadCredentialsFromFile(getProfilePath(name));
|
|
605
|
-
} catch {
|
|
606
|
-
return null;
|
|
607
|
-
}
|
|
845
|
+
chrome.kill();
|
|
846
|
+
};
|
|
847
|
+
return { kill };
|
|
608
848
|
}
|
|
609
849
|
|
|
610
|
-
// src/
|
|
611
|
-
import {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
async function saveConfig(outputDir, config) {
|
|
622
|
-
await mkdir3(outputDir, { recursive: true });
|
|
623
|
-
await writeFile3(join6(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
|
|
850
|
+
// src/hooks/useLogHistory.ts
|
|
851
|
+
import { useCallback, useState as useState3 } from "react";
|
|
852
|
+
function useLogHistory(limit) {
|
|
853
|
+
const [history, setHistory] = useState3([]);
|
|
854
|
+
const append = useCallback(
|
|
855
|
+
(message) => {
|
|
856
|
+
setHistory((h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-limit));
|
|
857
|
+
},
|
|
858
|
+
[limit]
|
|
859
|
+
);
|
|
860
|
+
return [history, append];
|
|
624
861
|
}
|
|
625
862
|
|
|
626
863
|
// src/ui/CredentialSetupUI.tsx
|
|
627
|
-
import { useCallback, useEffect as
|
|
628
|
-
import { Box as
|
|
629
|
-
import { jsx as
|
|
864
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useState as useState4 } from "react";
|
|
865
|
+
import { Box as Box4, Text as Text5, useInput } from "ink";
|
|
866
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
630
867
|
var FIELDS = ["apiKey", "userId", "businessId"];
|
|
631
868
|
var FIELD_LABELS = {
|
|
632
869
|
apiKey: "API Key",
|
|
633
870
|
userId: "User ID",
|
|
634
871
|
businessId: "Business ID"
|
|
635
872
|
};
|
|
636
|
-
var Hint = ({ text }) => /* @__PURE__ */
|
|
873
|
+
var Hint = ({ text }) => /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: text });
|
|
637
874
|
var CredentialSetupUI = ({
|
|
638
875
|
initialMode,
|
|
639
876
|
showProfileManager,
|
|
640
877
|
onComplete,
|
|
641
878
|
onCancel
|
|
642
879
|
}) => {
|
|
643
|
-
const [phase, setPhase] =
|
|
880
|
+
const [phase, setPhase] = useState4(
|
|
644
881
|
initialMode === "global" && !showProfileManager ? { type: "loading" } : initialMode === "global" && showProfileManager ? { type: "profile-loading" } : { type: "mode-select", cursor: 0 }
|
|
645
882
|
);
|
|
646
|
-
const [inputBuffer, setInputBuffer] =
|
|
647
|
-
const [error, setError] =
|
|
648
|
-
const [activeProfileName, setActiveProfileName] =
|
|
649
|
-
const handleModeChosen =
|
|
883
|
+
const [inputBuffer, setInputBuffer] = useState4("");
|
|
884
|
+
const [error, setError] = useState4(null);
|
|
885
|
+
const [activeProfileName, setActiveProfileName] = useState4(void 0);
|
|
886
|
+
const handleModeChosen = useCallback2(
|
|
650
887
|
(mode) => {
|
|
651
888
|
if (mode === "local") {
|
|
652
889
|
onComplete({ mode: "local", credentials: null });
|
|
@@ -656,7 +893,7 @@ var CredentialSetupUI = ({
|
|
|
656
893
|
},
|
|
657
894
|
[onComplete]
|
|
658
895
|
);
|
|
659
|
-
const handleProfileChosen =
|
|
896
|
+
const handleProfileChosen = useCallback2(async (profile) => {
|
|
660
897
|
if (profile === null) {
|
|
661
898
|
setPhase({ type: "profile-loading" });
|
|
662
899
|
const profiles = await listCredentialProfiles();
|
|
@@ -669,7 +906,7 @@ var CredentialSetupUI = ({
|
|
|
669
906
|
const envCursor2 = existing ? Math.max(0, ENVIRONMENTS.indexOf(existing.environment)) : 0;
|
|
670
907
|
setPhase({ type: "creds-entry", field: 0, values: existing ?? {}, envCursor: envCursor2 });
|
|
671
908
|
}, []);
|
|
672
|
-
const handleSave =
|
|
909
|
+
const handleSave = useCallback2(
|
|
673
910
|
async (values2, envCursor2) => {
|
|
674
911
|
const environment = ENVIRONMENTS[envCursor2] ?? "go";
|
|
675
912
|
const credentials = {
|
|
@@ -810,16 +1047,16 @@ var CredentialSetupUI = ({
|
|
|
810
1047
|
}
|
|
811
1048
|
}
|
|
812
1049
|
});
|
|
813
|
-
const loadProfileList =
|
|
1050
|
+
const loadProfileList = useCallback2(async () => {
|
|
814
1051
|
const profiles = await listCredentialProfiles();
|
|
815
1052
|
setPhase({ type: "profile-select", profiles, cursor: 0 });
|
|
816
1053
|
}, []);
|
|
817
|
-
|
|
1054
|
+
useEffect3(() => {
|
|
818
1055
|
if (phase.type === "profile-loading") {
|
|
819
1056
|
void loadProfileList();
|
|
820
1057
|
}
|
|
821
1058
|
}, [phase.type, loadProfileList]);
|
|
822
|
-
|
|
1059
|
+
useEffect3(() => {
|
|
823
1060
|
if (initialMode === "global" && !showProfileManager) {
|
|
824
1061
|
void (async () => {
|
|
825
1062
|
const existing = await loadGlobalCredentials();
|
|
@@ -828,14 +1065,14 @@ var CredentialSetupUI = ({
|
|
|
828
1065
|
})();
|
|
829
1066
|
}
|
|
830
1067
|
}, []);
|
|
831
|
-
|
|
1068
|
+
useEffect3(() => {
|
|
832
1069
|
if (phase.type === "creds-entry" && phase.field === 0) {
|
|
833
1070
|
setInputBuffer(phase.values.apiKey ?? "");
|
|
834
1071
|
}
|
|
835
1072
|
}, [phase.type]);
|
|
836
1073
|
if (phase.type === "loading" || phase.type === "saving" || phase.type === "profile-loading") {
|
|
837
1074
|
const label = phase.type === "saving" ? "Saving credentials\u2026" : phase.type === "profile-loading" ? "Loading profiles\u2026" : "Loading credentials\u2026";
|
|
838
|
-
return /* @__PURE__ */
|
|
1075
|
+
return /* @__PURE__ */ jsx5(Box4, { paddingY: 1, paddingX: 2, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: label }) });
|
|
839
1076
|
}
|
|
840
1077
|
if (phase.type === "done") {
|
|
841
1078
|
return null;
|
|
@@ -845,49 +1082,41 @@ var CredentialSetupUI = ({
|
|
|
845
1082
|
{ label: "Global", desc: `~/.kizenappbuilder/credentials.json`, mode: "global" },
|
|
846
1083
|
{ label: "Local", desc: "Enter credentials in the browser dev tools", mode: "local" }
|
|
847
1084
|
];
|
|
848
|
-
return /* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
852
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(24) })
|
|
853
|
-
] }),
|
|
854
|
-
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
|
|
855
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "Credential mode" }),
|
|
1085
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
1086
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
1087
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
1088
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Credential mode" }),
|
|
856
1089
|
options.map((opt, i) => {
|
|
857
1090
|
const selected = phase.cursor === i;
|
|
858
|
-
return /* @__PURE__ */
|
|
859
|
-
/* @__PURE__ */
|
|
860
|
-
/* @__PURE__ */
|
|
861
|
-
/* @__PURE__ */
|
|
1091
|
+
return /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
|
|
1092
|
+
/* @__PURE__ */ jsx5(Text5, { ...selected && { color: "cyan" }, children: selected ? "\u276F" : " " }),
|
|
1093
|
+
/* @__PURE__ */ jsx5(Text5, { bold: selected, ...selected && { color: "cyan" }, children: opt.label }),
|
|
1094
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: opt.desc })
|
|
862
1095
|
] }, opt.mode);
|
|
863
1096
|
})
|
|
864
1097
|
] }),
|
|
865
|
-
/* @__PURE__ */
|
|
1098
|
+
/* @__PURE__ */ jsx5(Hint, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit" })
|
|
866
1099
|
] });
|
|
867
1100
|
}
|
|
868
1101
|
if (phase.type === "profile-select") {
|
|
869
1102
|
const { profiles, cursor } = phase;
|
|
870
1103
|
const addNewIdx = profiles.length;
|
|
871
|
-
return /* @__PURE__ */
|
|
872
|
-
/* @__PURE__ */
|
|
873
|
-
/* @__PURE__ */
|
|
874
|
-
/* @__PURE__ */
|
|
875
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(24) })
|
|
876
|
-
] }),
|
|
877
|
-
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
|
|
878
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "Credential profiles" }),
|
|
1104
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
1105
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
1106
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
1107
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Credential profiles" }),
|
|
879
1108
|
profiles.map((profile, i) => {
|
|
880
1109
|
const selected = cursor === i;
|
|
881
|
-
return /* @__PURE__ */
|
|
882
|
-
/* @__PURE__ */
|
|
883
|
-
/* @__PURE__ */
|
|
884
|
-
/* @__PURE__ */
|
|
1110
|
+
return /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
|
|
1111
|
+
/* @__PURE__ */ jsx5(Text5, { ...selected && { color: "cyan" }, children: selected ? "\u276F" : " " }),
|
|
1112
|
+
/* @__PURE__ */ jsx5(Text5, { bold: selected, ...selected && { color: "cyan" }, children: profile.isDefault ? "Default" : profile.name }),
|
|
1113
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: profile.path })
|
|
885
1114
|
] }, profile.name);
|
|
886
1115
|
}),
|
|
887
|
-
/* @__PURE__ */
|
|
888
|
-
/* @__PURE__ */
|
|
889
|
-
/* @__PURE__ */
|
|
890
|
-
|
|
1116
|
+
/* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
|
|
1117
|
+
/* @__PURE__ */ jsx5(Text5, { ...cursor === addNewIdx && { color: "cyan" }, children: cursor === addNewIdx ? "\u276F" : " " }),
|
|
1118
|
+
/* @__PURE__ */ jsx5(
|
|
1119
|
+
Text5,
|
|
891
1120
|
{
|
|
892
1121
|
bold: cursor === addNewIdx,
|
|
893
1122
|
...cursor === addNewIdx && { color: "cyan" },
|
|
@@ -896,77 +1125,69 @@ var CredentialSetupUI = ({
|
|
|
896
1125
|
)
|
|
897
1126
|
] })
|
|
898
1127
|
] }),
|
|
899
|
-
/* @__PURE__ */
|
|
1128
|
+
/* @__PURE__ */ jsx5(Hint, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel" })
|
|
900
1129
|
] });
|
|
901
1130
|
}
|
|
902
1131
|
if (phase.type === "name-entry") {
|
|
903
1132
|
const { nameBuffer, nameError } = phase;
|
|
904
|
-
return /* @__PURE__ */
|
|
905
|
-
/* @__PURE__ */
|
|
906
|
-
/* @__PURE__ */
|
|
907
|
-
|
|
908
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(24) })
|
|
909
|
-
] }),
|
|
910
|
-
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", gap: 0, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: "New credential profile" }) }),
|
|
911
|
-
nameError && /* @__PURE__ */ jsxs2(Text3, { color: "red", children: [
|
|
1133
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
1134
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
1135
|
+
/* @__PURE__ */ jsx5(Box4, { flexDirection: "column", gap: 0, children: /* @__PURE__ */ jsx5(Text5, { bold: true, children: "New credential profile" }) }),
|
|
1136
|
+
nameError && /* @__PURE__ */ jsxs3(Text5, { color: "red", children: [
|
|
912
1137
|
"Error: ",
|
|
913
1138
|
nameError
|
|
914
1139
|
] }),
|
|
915
|
-
/* @__PURE__ */
|
|
916
|
-
/* @__PURE__ */
|
|
917
|
-
/* @__PURE__ */
|
|
918
|
-
/* @__PURE__ */
|
|
919
|
-
/* @__PURE__ */
|
|
1140
|
+
/* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
|
|
1141
|
+
/* @__PURE__ */ jsx5(Box4, { width: 12, children: /* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: "Profile name" }) }),
|
|
1142
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: ">" }),
|
|
1143
|
+
/* @__PURE__ */ jsx5(Text5, { children: nameBuffer }),
|
|
1144
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u2588" })
|
|
920
1145
|
] }),
|
|
921
|
-
/* @__PURE__ */
|
|
1146
|
+
/* @__PURE__ */ jsx5(Hint, { text: "Type a name \xB7 Enter to confirm \xB7 Esc to go back" })
|
|
922
1147
|
] });
|
|
923
1148
|
}
|
|
924
1149
|
const { field: activeField, values, envCursor } = phase;
|
|
925
1150
|
const isEnvField = activeField === FIELDS.length;
|
|
926
1151
|
const profileLabel = activeProfileName ?? DEFAULT_PROFILE_NAME;
|
|
927
|
-
return /* @__PURE__ */
|
|
928
|
-
/* @__PURE__ */
|
|
929
|
-
/* @__PURE__ */
|
|
930
|
-
/* @__PURE__ */
|
|
931
|
-
/* @__PURE__ */
|
|
932
|
-
] }),
|
|
933
|
-
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
|
|
934
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, children: activeProfileName !== void 0 ? `Profile: ${activeProfileName}` : "Global credentials" }),
|
|
935
|
-
/* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
|
|
1152
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
1153
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
1154
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
1155
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: activeProfileName !== void 0 ? `Profile: ${activeProfileName}` : "Global credentials" }),
|
|
1156
|
+
/* @__PURE__ */ jsxs3(Text5, { dimColor: true, children: [
|
|
936
1157
|
"Saved to:",
|
|
937
1158
|
" ",
|
|
938
1159
|
activeProfileName !== void 0 ? `~/.kizenappbuilder/${activeProfileName}.json` : GLOBAL_CREDENTIALS_PATH
|
|
939
1160
|
] })
|
|
940
1161
|
] }),
|
|
941
|
-
error && /* @__PURE__ */
|
|
1162
|
+
error && /* @__PURE__ */ jsxs3(Text5, { color: "red", children: [
|
|
942
1163
|
"Error: ",
|
|
943
1164
|
error
|
|
944
1165
|
] }),
|
|
945
|
-
/* @__PURE__ */
|
|
1166
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
946
1167
|
FIELDS.map((name, i) => {
|
|
947
1168
|
const isActive = activeField === i;
|
|
948
1169
|
const displayValue = isActive ? inputBuffer : values[name] ?? "";
|
|
949
|
-
return /* @__PURE__ */
|
|
950
|
-
/* @__PURE__ */
|
|
951
|
-
/* @__PURE__ */
|
|
952
|
-
/* @__PURE__ */
|
|
953
|
-
isActive && /* @__PURE__ */
|
|
1170
|
+
return /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
|
|
1171
|
+
/* @__PURE__ */ jsx5(Box4, { width: 12, children: /* @__PURE__ */ jsx5(Text5, { bold: isActive, ...isActive && { color: "cyan" }, children: FIELD_LABELS[name] }) }),
|
|
1172
|
+
/* @__PURE__ */ jsx5(Text5, { ...isActive && { color: "cyan" }, children: ">" }),
|
|
1173
|
+
/* @__PURE__ */ jsx5(Text5, { children: displayValue }),
|
|
1174
|
+
isActive && /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u2588" })
|
|
954
1175
|
] }, name);
|
|
955
1176
|
}),
|
|
956
|
-
/* @__PURE__ */
|
|
957
|
-
/* @__PURE__ */
|
|
958
|
-
/* @__PURE__ */
|
|
959
|
-
/* @__PURE__ */
|
|
1177
|
+
/* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
|
|
1178
|
+
/* @__PURE__ */ jsx5(Box4, { width: 12, children: /* @__PURE__ */ jsx5(Text5, { bold: isEnvField, ...isEnvField && { color: "cyan" }, children: "Environment" }) }),
|
|
1179
|
+
/* @__PURE__ */ jsx5(Text5, { ...isEnvField && { color: "cyan" }, children: ">" }),
|
|
1180
|
+
/* @__PURE__ */ jsx5(Box4, { gap: 1, children: ENVIRONMENTS.map((env, i) => {
|
|
960
1181
|
const isSelected = i === envCursor;
|
|
961
1182
|
if (isEnvField) {
|
|
962
|
-
return /* @__PURE__ */
|
|
1183
|
+
return /* @__PURE__ */ jsx5(Text5, { bold: isSelected, ...isSelected && { color: "cyan" }, children: isSelected ? `[${env}]` : env }, env);
|
|
963
1184
|
}
|
|
964
|
-
return /* @__PURE__ */
|
|
1185
|
+
return /* @__PURE__ */ jsx5(Text5, { dimColor: !isSelected, bold: isSelected, children: env }, env);
|
|
965
1186
|
}) })
|
|
966
1187
|
] })
|
|
967
1188
|
] }),
|
|
968
|
-
isEnvField ? /* @__PURE__ */
|
|
969
|
-
/* @__PURE__ */
|
|
1189
|
+
isEnvField ? /* @__PURE__ */ jsx5(Hint, { text: "\u2190\u2192 to select \xB7 \u2191\u2193 to move \xB7 Enter to save \xB7 Esc to cancel" }) : /* @__PURE__ */ jsx5(Hint, { text: "\u2191\u2193 to move \xB7 Backspace to delete \xB7 Esc to cancel" }),
|
|
1190
|
+
/* @__PURE__ */ jsxs3(Text5, { dimColor: true, children: [
|
|
970
1191
|
"Profile: ",
|
|
971
1192
|
profileLabel
|
|
972
1193
|
] })
|
|
@@ -974,202 +1195,8 @@ var CredentialSetupUI = ({
|
|
|
974
1195
|
};
|
|
975
1196
|
|
|
976
1197
|
// src/ui/DevUI.tsx
|
|
977
|
-
import { jsx as
|
|
978
|
-
function proxyLogEntryToString(entry) {
|
|
979
|
-
if (entry.kind === "info") {
|
|
980
|
-
return entry.message;
|
|
981
|
-
}
|
|
982
|
-
const cache = entry.fromCache ? " [cache]" : "";
|
|
983
|
-
return `${entry.method} ${entry.url} \u2192 ${String(entry.status)}${cache}`;
|
|
984
|
-
}
|
|
1198
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
985
1199
|
var SKIP_WATCH_PREFIXES = [".kizenapp", ".git"];
|
|
986
|
-
var LOG_LIMIT = 50;
|
|
987
|
-
var LOG_DISPLAY = 8;
|
|
988
|
-
var MIME_TYPES = {
|
|
989
|
-
".html": "text/html; charset=utf-8",
|
|
990
|
-
".js": "application/javascript; charset=utf-8",
|
|
991
|
-
".mjs": "application/javascript; charset=utf-8",
|
|
992
|
-
".css": "text/css; charset=utf-8",
|
|
993
|
-
".png": "image/png",
|
|
994
|
-
".jpg": "image/jpeg",
|
|
995
|
-
".svg": "image/svg+xml",
|
|
996
|
-
".ico": "image/x-icon",
|
|
997
|
-
".json": "application/json; charset=utf-8",
|
|
998
|
-
".woff": "font/woff",
|
|
999
|
-
".woff2": "font/woff2",
|
|
1000
|
-
".ttf": "font/ttf",
|
|
1001
|
-
".map": "application/json"
|
|
1002
|
-
};
|
|
1003
|
-
var SPINNER_FRAMES2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1004
|
-
var Spinner2 = () => {
|
|
1005
|
-
const [frame, setFrame] = useState3(0);
|
|
1006
|
-
useEffect3(() => {
|
|
1007
|
-
const id = setInterval(() => {
|
|
1008
|
-
setFrame((prev) => (prev + 1) % SPINNER_FRAMES2.length);
|
|
1009
|
-
}, 80);
|
|
1010
|
-
return () => {
|
|
1011
|
-
clearInterval(id);
|
|
1012
|
-
};
|
|
1013
|
-
}, []);
|
|
1014
|
-
return /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: SPINNER_FRAMES2[frame] ?? "\u280B" });
|
|
1015
|
-
};
|
|
1016
|
-
function getViewerPath() {
|
|
1017
|
-
const filename = fileURLToPath2(import.meta.url);
|
|
1018
|
-
return join7(dirname3(filename), "viewer");
|
|
1019
|
-
}
|
|
1020
|
-
async function fileExists(filePath) {
|
|
1021
|
-
try {
|
|
1022
|
-
await access(filePath);
|
|
1023
|
-
return true;
|
|
1024
|
-
} catch {
|
|
1025
|
-
return false;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast, onProxyCacheChange) {
|
|
1029
|
-
const proxyCache = createProxyCache({ onChange: onProxyCacheChange });
|
|
1030
|
-
return (req, res) => {
|
|
1031
|
-
void (async () => {
|
|
1032
|
-
const url = req.url ?? "/";
|
|
1033
|
-
try {
|
|
1034
|
-
createServerLog(`Received request: ${url}`);
|
|
1035
|
-
if (url === "/api/credentials") {
|
|
1036
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1037
|
-
res.end(credentialsRef.current !== null ? JSON.stringify(credentialsRef.current) : "{}");
|
|
1038
|
-
return;
|
|
1039
|
-
}
|
|
1040
|
-
if (url === "/api/credential-profiles" && req.method === "GET") {
|
|
1041
|
-
const profiles = await listCredentialProfiles();
|
|
1042
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1043
|
-
res.end(JSON.stringify({ profiles, active: activeProfileRef.current }));
|
|
1044
|
-
return;
|
|
1045
|
-
}
|
|
1046
|
-
if (url === "/api/credentials/switch" && req.method === "POST") {
|
|
1047
|
-
const chunks = [];
|
|
1048
|
-
for await (const chunk of req) {
|
|
1049
|
-
chunks.push(chunk);
|
|
1050
|
-
}
|
|
1051
|
-
const body = JSON.parse(Buffer.concat(chunks).toString("utf-8") || "{}");
|
|
1052
|
-
const profileName = body.profile;
|
|
1053
|
-
const creds = profileName === void 0 || profileName === "" ? await loadGlobalCredentials() : await loadCredentialProfile(profileName);
|
|
1054
|
-
if (creds !== null) {
|
|
1055
|
-
credentialsRef.current = creds;
|
|
1056
|
-
activeProfileRef.current = profileName ?? void 0;
|
|
1057
|
-
const active = activeProfileRef.current;
|
|
1058
|
-
await saveConfig(outputDir, {
|
|
1059
|
-
credentialMode: "global",
|
|
1060
|
-
...active !== void 0 && { activeCredentialProfile: active }
|
|
1061
|
-
});
|
|
1062
|
-
broadcast({ type: "credentials-updated", credentials: creds });
|
|
1063
|
-
}
|
|
1064
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1065
|
-
res.end(JSON.stringify({ ok: creds !== null }));
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
if (url === "/api/bundle") {
|
|
1069
|
-
const bundlePath = join7(process.cwd(), ".kizenapp", "bundle.json");
|
|
1070
|
-
try {
|
|
1071
|
-
const content = await readFile5(bundlePath, "utf-8");
|
|
1072
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1073
|
-
res.end(content);
|
|
1074
|
-
} catch {
|
|
1075
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1076
|
-
res.end("{}");
|
|
1077
|
-
}
|
|
1078
|
-
return;
|
|
1079
|
-
}
|
|
1080
|
-
if (url === "/api/proxy-cache/clear") {
|
|
1081
|
-
proxyCache.clear();
|
|
1082
|
-
createProxyLog({ kind: "info", message: "Proxy cache cleared" });
|
|
1083
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1084
|
-
res.end('{"ok":true}');
|
|
1085
|
-
return;
|
|
1086
|
-
}
|
|
1087
|
-
if (url.startsWith("/api/proxy")) {
|
|
1088
|
-
const proxyTarget = req.headers["x-proxy-target"];
|
|
1089
|
-
if (typeof proxyTarget !== "string") {
|
|
1090
|
-
res.writeHead(400);
|
|
1091
|
-
res.end("Missing x-proxy-target header");
|
|
1092
|
-
return;
|
|
1093
|
-
}
|
|
1094
|
-
const upstreamPath = url.slice("/api/proxy".length) || "/";
|
|
1095
|
-
const upstreamUrl = `${proxyTarget}${upstreamPath}`;
|
|
1096
|
-
const method = req.method ?? "GET";
|
|
1097
|
-
const chunks = [];
|
|
1098
|
-
for await (const chunk of req) {
|
|
1099
|
-
chunks.push(chunk);
|
|
1100
|
-
}
|
|
1101
|
-
const { host, "x-proxy-target": _drop, ...forwardHeaders } = req.headers;
|
|
1102
|
-
if (method === "GET") {
|
|
1103
|
-
const cacheKey = `GET::${upstreamUrl}`;
|
|
1104
|
-
const { response: cached, fromCache } = await proxyCache.get(
|
|
1105
|
-
cacheKey,
|
|
1106
|
-
() => fetch(upstreamUrl, {
|
|
1107
|
-
method: "GET",
|
|
1108
|
-
headers: forwardHeaders
|
|
1109
|
-
})
|
|
1110
|
-
);
|
|
1111
|
-
createProxyLog({
|
|
1112
|
-
kind: "request",
|
|
1113
|
-
method: "GET",
|
|
1114
|
-
status: cached.status,
|
|
1115
|
-
fromCache,
|
|
1116
|
-
url: upstreamPath
|
|
1117
|
-
});
|
|
1118
|
-
res.writeHead(cached.status, cached.headers);
|
|
1119
|
-
res.end(cached.body);
|
|
1120
|
-
return;
|
|
1121
|
-
}
|
|
1122
|
-
const body = chunks.length > 0 ? Buffer.concat(chunks) : void 0;
|
|
1123
|
-
const resolvedBody = body && body.length > 0 ? body : void 0;
|
|
1124
|
-
const upstream = await fetch(upstreamUrl, {
|
|
1125
|
-
method,
|
|
1126
|
-
headers: forwardHeaders,
|
|
1127
|
-
...resolvedBody !== void 0 && { body: resolvedBody }
|
|
1128
|
-
});
|
|
1129
|
-
createProxyLog({
|
|
1130
|
-
kind: "request",
|
|
1131
|
-
method,
|
|
1132
|
-
status: upstream.status,
|
|
1133
|
-
fromCache: false,
|
|
1134
|
-
url: upstreamPath
|
|
1135
|
-
});
|
|
1136
|
-
const responseHeaders = Object.fromEntries(upstream.headers);
|
|
1137
|
-
delete responseHeaders["content-encoding"];
|
|
1138
|
-
delete responseHeaders["content-length"];
|
|
1139
|
-
res.writeHead(upstream.status, responseHeaders);
|
|
1140
|
-
res.end(Buffer.from(await upstream.arrayBuffer()));
|
|
1141
|
-
return;
|
|
1142
|
-
}
|
|
1143
|
-
const rawPath = url === "/" ? "/index.html" : url;
|
|
1144
|
-
const filePath = join7(viewerPath, rawPath);
|
|
1145
|
-
const resolvedPath = await fileExists(filePath) ? filePath : join7(viewerPath, "index.html");
|
|
1146
|
-
const ext = extname(resolvedPath);
|
|
1147
|
-
const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
1148
|
-
res.writeHead(200, { "Content-Type": mimeType });
|
|
1149
|
-
createReadStream(resolvedPath).pipe(res);
|
|
1150
|
-
} catch (err) {
|
|
1151
|
-
if (url.startsWith("/api/proxy")) {
|
|
1152
|
-
createProxyLog({
|
|
1153
|
-
kind: "request",
|
|
1154
|
-
method: req.method ?? "GET",
|
|
1155
|
-
status: 502,
|
|
1156
|
-
fromCache: false,
|
|
1157
|
-
url: url.slice("/api/proxy".length) || "/"
|
|
1158
|
-
});
|
|
1159
|
-
} else {
|
|
1160
|
-
createProxyLog({
|
|
1161
|
-
kind: "info",
|
|
1162
|
-
message: `Error handling ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
1163
|
-
});
|
|
1164
|
-
}
|
|
1165
|
-
if (!res.headersSent) {
|
|
1166
|
-
res.writeHead(502);
|
|
1167
|
-
res.end("Bad Gateway");
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
})();
|
|
1171
|
-
};
|
|
1172
|
-
}
|
|
1173
1200
|
var DevUI = ({
|
|
1174
1201
|
port,
|
|
1175
1202
|
pluginDir,
|
|
@@ -1180,29 +1207,30 @@ var DevUI = ({
|
|
|
1180
1207
|
}) => {
|
|
1181
1208
|
const credentialsRef = useRef(initialCredentials);
|
|
1182
1209
|
const activeProfileRef = useRef(initialActiveProfile);
|
|
1183
|
-
const [credMode, setCredMode] =
|
|
1184
|
-
const [credentialMode, setCredentialMode] =
|
|
1210
|
+
const [credMode, setCredMode] = useState5("main");
|
|
1211
|
+
const [credentialMode, setCredentialMode] = useState5(
|
|
1185
1212
|
initialCredentialMode
|
|
1186
1213
|
);
|
|
1187
|
-
const [status, setStatus] =
|
|
1188
|
-
const [errorMessage, setErrorMessage] =
|
|
1189
|
-
const [serverLogHistory,
|
|
1190
|
-
const [buildLogHistory,
|
|
1191
|
-
const [proxyLogHistory, setProxyLogHistory] =
|
|
1192
|
-
const [buildStatus, setBuildStatus] =
|
|
1193
|
-
const [buildError, setBuildError] =
|
|
1194
|
-
const [lastBuilt, setLastBuilt] =
|
|
1195
|
-
const [
|
|
1196
|
-
const [
|
|
1197
|
-
const [
|
|
1198
|
-
const [
|
|
1214
|
+
const [status, setStatus] = useState5("starting");
|
|
1215
|
+
const [errorMessage, setErrorMessage] = useState5(null);
|
|
1216
|
+
const [serverLogHistory, appendServerLog] = useLogHistory(LOG_LIMIT);
|
|
1217
|
+
const [buildLogHistory, appendBuildLog] = useLogHistory(LOG_LIMIT);
|
|
1218
|
+
const [proxyLogHistory, setProxyLogHistory] = useState5([]);
|
|
1219
|
+
const [buildStatus, setBuildStatus] = useState5("pending");
|
|
1220
|
+
const [buildError, setBuildError] = useState5(null);
|
|
1221
|
+
const [lastBuilt, setLastBuilt] = useState5(null);
|
|
1222
|
+
const [lastBundleSize, setLastBundleSize] = useState5(null);
|
|
1223
|
+
const [wsClientCount, setWsClientCount] = useState5(0);
|
|
1224
|
+
const [proxyCacheSize, setProxyCacheSize] = useState5(0);
|
|
1225
|
+
const [proxyCacheHits, setProxyCacheHits] = useState5(0);
|
|
1226
|
+
const [proxyCacheMisses, setProxyCacheMisses] = useState5(0);
|
|
1199
1227
|
const buildingRef = useRef(false);
|
|
1200
1228
|
const viewerLaunchedRef = useRef(false);
|
|
1201
1229
|
const chromeHandleRef = useRef(null);
|
|
1202
1230
|
const debounceTimerRef = useRef(null);
|
|
1203
1231
|
const wsClientsRef = useRef(/* @__PURE__ */ new Set());
|
|
1204
1232
|
const pendingMessagesRef = useRef([]);
|
|
1205
|
-
const broadcast =
|
|
1233
|
+
const broadcast = useCallback3((msg) => {
|
|
1206
1234
|
const json = JSON.stringify(msg);
|
|
1207
1235
|
if (wsClientsRef.current.size === 0) {
|
|
1208
1236
|
pendingMessagesRef.current = [...pendingMessagesRef.current, json].slice(-LOG_LIMIT);
|
|
@@ -1214,17 +1242,13 @@ var DevUI = ({
|
|
|
1214
1242
|
}
|
|
1215
1243
|
}
|
|
1216
1244
|
}, []);
|
|
1217
|
-
const createServerLog =
|
|
1218
|
-
|
|
1219
|
-
(h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-LOG_LIMIT)
|
|
1220
|
-
);
|
|
1221
|
-
}, []);
|
|
1222
|
-
const launchChromeViewerCb = useCallback2(() => {
|
|
1245
|
+
const createServerLog = appendServerLog;
|
|
1246
|
+
const launchChromeViewerCb = useCallback3(() => {
|
|
1223
1247
|
chromeHandleRef.current?.kill();
|
|
1224
1248
|
chromeHandleRef.current = null;
|
|
1225
1249
|
void launchChromeViewer({
|
|
1226
1250
|
port,
|
|
1227
|
-
userDataDir:
|
|
1251
|
+
userDataDir: join8(outputDir, ".chrome"),
|
|
1228
1252
|
broadcast
|
|
1229
1253
|
}).then((handle) => {
|
|
1230
1254
|
chromeHandleRef.current = handle;
|
|
@@ -1247,14 +1271,14 @@ var DevUI = ({
|
|
|
1247
1271
|
},
|
|
1248
1272
|
{ isActive: credMode === "main" }
|
|
1249
1273
|
);
|
|
1250
|
-
|
|
1274
|
+
useEffect4(() => {
|
|
1251
1275
|
if (status !== "running" || viewerLaunchedRef.current) {
|
|
1252
1276
|
return;
|
|
1253
1277
|
}
|
|
1254
1278
|
viewerLaunchedRef.current = true;
|
|
1255
1279
|
launchChromeViewerCb();
|
|
1256
1280
|
}, [status, launchChromeViewerCb]);
|
|
1257
|
-
const handleCredentialsDone =
|
|
1281
|
+
const handleCredentialsDone = useCallback3(
|
|
1258
1282
|
(result) => {
|
|
1259
1283
|
credentialsRef.current = result.credentials;
|
|
1260
1284
|
activeProfileRef.current = result.profileName;
|
|
@@ -1269,7 +1293,7 @@ var DevUI = ({
|
|
|
1269
1293
|
},
|
|
1270
1294
|
[outputDir, broadcast]
|
|
1271
1295
|
);
|
|
1272
|
-
const createProxyLog =
|
|
1296
|
+
const createProxyLog = useCallback3(
|
|
1273
1297
|
(entry) => {
|
|
1274
1298
|
setProxyLogHistory(
|
|
1275
1299
|
(h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${proxyLogEntryToString(entry)}`].slice(
|
|
@@ -1287,16 +1311,14 @@ var DevUI = ({
|
|
|
1287
1311
|
},
|
|
1288
1312
|
[broadcast]
|
|
1289
1313
|
);
|
|
1290
|
-
const createBuildLog =
|
|
1314
|
+
const createBuildLog = useCallback3(
|
|
1291
1315
|
(message) => {
|
|
1292
|
-
|
|
1293
|
-
(h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-LOG_LIMIT)
|
|
1294
|
-
);
|
|
1316
|
+
appendBuildLog(message);
|
|
1295
1317
|
broadcast({ type: "log", message });
|
|
1296
1318
|
},
|
|
1297
|
-
[broadcast]
|
|
1319
|
+
[appendBuildLog, broadcast]
|
|
1298
1320
|
);
|
|
1299
|
-
const triggerBuild =
|
|
1321
|
+
const triggerBuild = useCallback3(() => {
|
|
1300
1322
|
if (buildingRef.current) {
|
|
1301
1323
|
return;
|
|
1302
1324
|
}
|
|
@@ -1304,10 +1326,11 @@ var DevUI = ({
|
|
|
1304
1326
|
setBuildStatus("building");
|
|
1305
1327
|
setBuildError(null);
|
|
1306
1328
|
createBuildLog("Build started");
|
|
1307
|
-
void runBuild(pluginDir, outputDir).then(() => {
|
|
1308
|
-
createBuildLog(
|
|
1329
|
+
void runBuild(pluginDir, outputDir).then(({ bundleSize }) => {
|
|
1330
|
+
createBuildLog(`Build finished \u2014 bundle: ${formatBytes(bundleSize)}`);
|
|
1309
1331
|
setBuildStatus("done");
|
|
1310
1332
|
setLastBuilt(/* @__PURE__ */ new Date());
|
|
1333
|
+
setLastBundleSize(bundleSize);
|
|
1311
1334
|
createBuildLog("Notifying viewers to reload");
|
|
1312
1335
|
for (const client of wsClientsRef.current) {
|
|
1313
1336
|
if (client.readyState === 1) {
|
|
@@ -1322,7 +1345,7 @@ var DevUI = ({
|
|
|
1322
1345
|
buildingRef.current = false;
|
|
1323
1346
|
});
|
|
1324
1347
|
}, [pluginDir, outputDir, createBuildLog]);
|
|
1325
|
-
|
|
1348
|
+
useEffect4(() => {
|
|
1326
1349
|
triggerBuild();
|
|
1327
1350
|
const watcher = watch(pluginDir, { recursive: true }, (_, filename) => {
|
|
1328
1351
|
if (!filename) {
|
|
@@ -1336,7 +1359,7 @@ var DevUI = ({
|
|
|
1336
1359
|
if (debounceTimerRef.current !== null) {
|
|
1337
1360
|
clearTimeout(debounceTimerRef.current);
|
|
1338
1361
|
}
|
|
1339
|
-
debounceTimerRef.current = setTimeout(triggerBuild,
|
|
1362
|
+
debounceTimerRef.current = setTimeout(triggerBuild, FILE_WATCH_DEBOUNCE_MS);
|
|
1340
1363
|
});
|
|
1341
1364
|
return () => {
|
|
1342
1365
|
watcher.close();
|
|
@@ -1345,9 +1368,9 @@ var DevUI = ({
|
|
|
1345
1368
|
}
|
|
1346
1369
|
};
|
|
1347
1370
|
}, [pluginDir, triggerBuild, createBuildLog]);
|
|
1348
|
-
|
|
1371
|
+
useEffect4(() => {
|
|
1349
1372
|
const viewerPath = getViewerPath();
|
|
1350
|
-
void fileExists(
|
|
1373
|
+
void fileExists(join8(viewerPath, "index.html")).then((viewerBuilt) => {
|
|
1351
1374
|
if (!viewerBuilt) {
|
|
1352
1375
|
setErrorMessage("Viewer not built. Run 'pnpm build:viewer' first.");
|
|
1353
1376
|
setStatus("error");
|
|
@@ -1395,7 +1418,7 @@ var DevUI = ({
|
|
|
1395
1418
|
});
|
|
1396
1419
|
}, [port, createServerLog, createProxyLog, broadcast, outputDir]);
|
|
1397
1420
|
if (credMode === "editing") {
|
|
1398
|
-
return /* @__PURE__ */
|
|
1421
|
+
return /* @__PURE__ */ jsx6(
|
|
1399
1422
|
CredentialSetupUI,
|
|
1400
1423
|
{
|
|
1401
1424
|
...credentialMode !== void 0 && { initialMode: credentialMode },
|
|
@@ -1409,80 +1432,80 @@ var DevUI = ({
|
|
|
1409
1432
|
}
|
|
1410
1433
|
const elapsedSeconds = lastBuilt !== null ? Math.round((Date.now() - lastBuilt.getTime()) / 1e3) : null;
|
|
1411
1434
|
const elapsedLabel = elapsedSeconds === null ? "" : elapsedSeconds < 5 ? " (just now)" : ` (${String(elapsedSeconds)}s ago)`;
|
|
1412
|
-
return /* @__PURE__ */
|
|
1413
|
-
/* @__PURE__ */
|
|
1414
|
-
/* @__PURE__ */
|
|
1415
|
-
/* @__PURE__ */
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
buildStatus === "pending" && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "waiting\u2026" }),
|
|
1422
|
-
buildStatus === "building" && /* @__PURE__ */ jsxs3(Box4, { gap: 1, children: [
|
|
1423
|
-
/* @__PURE__ */ jsx4(Spinner2, {}),
|
|
1424
|
-
/* @__PURE__ */ jsx4(Text4, { children: "Building plugin package\u2026" })
|
|
1435
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", paddingY: 1, paddingX: 2, children: [
|
|
1436
|
+
/* @__PURE__ */ jsx6(AppHeader, { marginBottom: 1 }),
|
|
1437
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
1438
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 2, marginBottom: 0, children: [
|
|
1439
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, dimColor: true, children: "App Build" }),
|
|
1440
|
+
buildStatus === "pending" && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "waiting\u2026" }),
|
|
1441
|
+
buildStatus === "building" && /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
1442
|
+
/* @__PURE__ */ jsx6(Spinner, {}),
|
|
1443
|
+
/* @__PURE__ */ jsx6(Text6, { children: "Building plugin package\u2026" })
|
|
1425
1444
|
] }),
|
|
1426
|
-
buildStatus === "done" && /* @__PURE__ */
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
lastBuilt !== null && /* @__PURE__ */
|
|
1445
|
+
buildStatus === "done" && /* @__PURE__ */ jsxs4(Box5, { gap: 1, children: [
|
|
1446
|
+
/* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713 Built" }),
|
|
1447
|
+
lastBuilt !== null && /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
1429
1448
|
"at ",
|
|
1430
1449
|
lastBuilt.toLocaleTimeString(),
|
|
1431
1450
|
elapsedLabel
|
|
1451
|
+
] }),
|
|
1452
|
+
lastBundleSize !== null && /* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
1453
|
+
"\xB7 bundle: ",
|
|
1454
|
+
formatBytes(lastBundleSize)
|
|
1432
1455
|
] })
|
|
1433
1456
|
] }),
|
|
1434
|
-
buildStatus === "error" && /* @__PURE__ */
|
|
1457
|
+
buildStatus === "error" && /* @__PURE__ */ jsxs4(Text6, { color: "red", children: [
|
|
1435
1458
|
"\u2717 ",
|
|
1436
1459
|
buildError ?? "unknown error"
|
|
1437
1460
|
] })
|
|
1438
1461
|
] }),
|
|
1439
|
-
/* @__PURE__ */
|
|
1440
|
-
|
|
1462
|
+
/* @__PURE__ */ jsx6(
|
|
1463
|
+
Box5,
|
|
1441
1464
|
{
|
|
1442
1465
|
flexDirection: "column",
|
|
1443
1466
|
height: LOG_DISPLAY + 2,
|
|
1444
1467
|
borderStyle: "single",
|
|
1445
1468
|
borderColor: "gray",
|
|
1446
1469
|
overflow: "hidden",
|
|
1447
|
-
children: buildLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */
|
|
1470
|
+
children: buildLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate", children: log }, index))
|
|
1448
1471
|
}
|
|
1449
1472
|
)
|
|
1450
1473
|
] }),
|
|
1451
|
-
/* @__PURE__ */
|
|
1452
|
-
/* @__PURE__ */
|
|
1453
|
-
/* @__PURE__ */
|
|
1454
|
-
status === "starting" && /* @__PURE__ */
|
|
1455
|
-
status === "running" && /* @__PURE__ */
|
|
1456
|
-
/* @__PURE__ */
|
|
1457
|
-
/* @__PURE__ */
|
|
1474
|
+
/* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
1475
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
1476
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, dimColor: true, children: "Web Server" }),
|
|
1477
|
+
status === "starting" && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "starting\u2026" }),
|
|
1478
|
+
status === "running" && /* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
1479
|
+
/* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713 Running" }),
|
|
1480
|
+
/* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
1458
1481
|
"\u25CF ",
|
|
1459
1482
|
wsClientCount,
|
|
1460
1483
|
" viewer",
|
|
1461
1484
|
wsClientCount !== 1 ? "s" : ""
|
|
1462
1485
|
] })
|
|
1463
1486
|
] }),
|
|
1464
|
-
status === "error" && /* @__PURE__ */
|
|
1487
|
+
status === "error" && /* @__PURE__ */ jsxs4(Text6, { color: "red", children: [
|
|
1465
1488
|
"\u2717 ",
|
|
1466
1489
|
errorMessage ?? "Server error"
|
|
1467
1490
|
] })
|
|
1468
1491
|
] }),
|
|
1469
|
-
/* @__PURE__ */
|
|
1470
|
-
|
|
1492
|
+
/* @__PURE__ */ jsx6(
|
|
1493
|
+
Box5,
|
|
1471
1494
|
{
|
|
1472
1495
|
flexDirection: "column",
|
|
1473
1496
|
height: LOG_DISPLAY + 2,
|
|
1474
1497
|
borderStyle: "single",
|
|
1475
1498
|
borderColor: "gray",
|
|
1476
1499
|
overflow: "hidden",
|
|
1477
|
-
children: serverLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */
|
|
1500
|
+
children: serverLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate", children: log }, index))
|
|
1478
1501
|
}
|
|
1479
1502
|
)
|
|
1480
1503
|
] }),
|
|
1481
|
-
/* @__PURE__ */
|
|
1482
|
-
/* @__PURE__ */
|
|
1483
|
-
/* @__PURE__ */
|
|
1484
|
-
/* @__PURE__ */
|
|
1485
|
-
/* @__PURE__ */
|
|
1504
|
+
/* @__PURE__ */ jsxs4(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
1505
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
1506
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, dimColor: true, children: "Network Proxy" }),
|
|
1507
|
+
/* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713 Active" }),
|
|
1508
|
+
/* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
1486
1509
|
"\u25CF ",
|
|
1487
1510
|
proxyCacheSize,
|
|
1488
1511
|
" key",
|
|
@@ -1495,26 +1518,26 @@ var DevUI = ({
|
|
|
1495
1518
|
" miss"
|
|
1496
1519
|
] })
|
|
1497
1520
|
] }),
|
|
1498
|
-
/* @__PURE__ */
|
|
1499
|
-
|
|
1521
|
+
/* @__PURE__ */ jsx6(
|
|
1522
|
+
Box5,
|
|
1500
1523
|
{
|
|
1501
1524
|
flexDirection: "column",
|
|
1502
1525
|
height: LOG_DISPLAY + 2,
|
|
1503
1526
|
borderStyle: "single",
|
|
1504
1527
|
borderColor: "gray",
|
|
1505
1528
|
overflow: "hidden",
|
|
1506
|
-
children: proxyLogHistory.length === 0 ? /* @__PURE__ */
|
|
1529
|
+
children: proxyLogHistory.length === 0 ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " No requests yet." }) : proxyLogHistory.slice(-LOG_DISPLAY).map((log, i) => /* @__PURE__ */ jsx6(Text6, { dimColor: true, wrap: "truncate", children: log }, i))
|
|
1507
1530
|
}
|
|
1508
1531
|
)
|
|
1509
1532
|
] }),
|
|
1510
|
-
/* @__PURE__ */
|
|
1511
|
-
/* @__PURE__ */
|
|
1512
|
-
/* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
/* @__PURE__ */
|
|
1515
|
-
/* @__PURE__ */
|
|
1516
|
-
/* @__PURE__ */
|
|
1517
|
-
/* @__PURE__ */
|
|
1533
|
+
/* @__PURE__ */ jsxs4(Box5, { marginTop: 1, gap: 1, children: [
|
|
1534
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "[" }),
|
|
1535
|
+
/* @__PURE__ */ jsx6(Text6, { children: "q" }),
|
|
1536
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "] quit [" }),
|
|
1537
|
+
/* @__PURE__ */ jsx6(Text6, { children: "c" }),
|
|
1538
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "] credentials [" }),
|
|
1539
|
+
/* @__PURE__ */ jsx6(Text6, { children: "v" }),
|
|
1540
|
+
/* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
1518
1541
|
"] ",
|
|
1519
1542
|
wsClientCount === 0 ? "open viewer" : "relaunch viewer"
|
|
1520
1543
|
] })
|