@kizenapps/cli 0.12.0 → 0.13.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/index.js +1374 -605
- package/dist/index.js.map +1 -1
- package/dist/viewer/assets/AppDetailPage-Bh4FLKqJ.js +1 -0
- package/dist/viewer/assets/AppDetailPage-BoFB93TE.js +1 -0
- package/dist/viewer/assets/AppDetailPage-Ck_VyLCS.js +1 -0
- package/dist/viewer/assets/BootstrapContext-CL_onTG7.js +1 -0
- package/dist/viewer/assets/BootstrapContext-c_1rWAfc.js +1 -0
- package/dist/viewer/assets/Card-BpDJPrPZ.js +1 -0
- package/dist/viewer/assets/Card-C7KVwKfm.js +1 -0
- package/dist/viewer/assets/CodeStepsPage-BHbbvjvr.js +1 -0
- package/dist/viewer/assets/CodeStepsPage-DnjVkPGB.js +1 -0
- package/dist/viewer/assets/CodeStepsPage-vHoGY4qA.js +1 -0
- package/dist/viewer/assets/CodeViewer--wbTev7Q.js +4 -0
- package/dist/viewer/assets/CodeViewer-Cm7Th9pG.js +4 -0
- package/dist/viewer/assets/ConfigurationPage-DAVSSfuO.js +1 -0
- package/dist/viewer/assets/ConfigurationPage-_PScSEXP.js +1 -0
- package/dist/viewer/assets/ConfigurationPage-zSJDg1f4.js +1 -0
- package/dist/viewer/assets/CredentialsContext-Rkt9WDIb.js +1 -0
- package/dist/viewer/assets/CredentialsContext-k-q4M-VC.js +1 -0
- package/dist/viewer/assets/{DevSidebar-BhTD2bBO.js → DevSidebar-CKdTotWS.js} +1 -1
- package/dist/viewer/assets/DevSidebar-CcSTChgY.js +1 -0
- package/dist/viewer/assets/IconReferencePage-BhQv3kD4.js +1 -0
- package/dist/viewer/assets/IconReferencePage-C_I2Fxnr.js +1 -0
- package/dist/viewer/assets/PluginIcon-CWkWM-Eu.js +1 -0
- package/dist/viewer/assets/SandboxPage-BMz04QVs.js +1 -0
- package/dist/viewer/assets/SandboxPage-BWtQb8kY.js +1 -0
- package/dist/viewer/assets/SandboxPage-DOBUc8VL.js +1 -0
- package/dist/viewer/assets/SidebarContext-Cn8QRb77.js +1 -0
- package/dist/viewer/assets/SidebarContext-DMhpPBf_.js +1 -0
- package/dist/viewer/assets/SourceBrowserPage-C-IhTiL0.js +1 -0
- package/dist/viewer/assets/SourceBrowserPage-Cr6XOIdV.js +1 -0
- package/dist/viewer/assets/VersionsPage-Bpj4_jPC.js +1 -0
- package/dist/viewer/assets/VersionsPage-DAm4Ozs0.js +1 -0
- package/dist/viewer/assets/VersionsPage-T8tsqgQx.js +1 -0
- package/dist/viewer/assets/{api-BOqcXygX.js → api-DFDquUsn.js} +1 -1
- package/dist/viewer/assets/{api-DqAiw0w9.js → api-bszpvP9L.js} +1 -1
- package/dist/viewer/assets/bundleQuery-BQLpZ06V.js +1 -0
- package/dist/viewer/assets/bundleQuery-CjbA01rb.js +1 -0
- package/dist/viewer/assets/fa-core-BoBqCOD3.js +558 -0
- package/dist/viewer/assets/{fa-core-wsCxaNB7.js → fa-core-C4eeAd4v.js} +1 -1
- package/dist/viewer/assets/formatBytes-BCP0hIUu.js +1 -0
- package/dist/viewer/assets/index-7CwR8Ogd.js +5 -0
- package/dist/viewer/assets/index-CCB9tECd.css +2 -0
- package/dist/viewer/assets/index-FEIsXxbf.js +5 -0
- package/dist/viewer/assets/index-fU82pXSW.js +5 -0
- package/dist/viewer/assets/{react-CyWK9rg5.js → react-CmC5kGa_.js} +1 -1
- package/dist/viewer/assets/{react-CkuOm_dq.js → react-csXWLXSE.js} +1 -1
- package/dist/viewer/assets/{recordDetail.worker-BIHPFt8Y.js → recordDetail.worker-C0-mQTaj.js} +1 -1
- package/dist/viewer/assets/useCriticalExceptionDialog-BeVl0uaO.js +14 -0
- package/dist/viewer/assets/useCriticalExceptionDialog-DseJ_u9B.js +14 -0
- package/dist/viewer/assets/useCurrentApp-CWtSEVC4.js +1 -0
- package/dist/viewer/assets/useCurrentApp-x1p8URhM.js +1 -0
- package/dist/viewer/assets/{useLocalStorage-BWwvt0nG.js → useLocalStorage-HYhrPUxS.js} +1 -1
- package/dist/viewer/assets/{useLocalStorage-BlZWItfY.js → useLocalStorage-eJ1PxgAP.js} +1 -1
- package/dist/viewer/assets/{useNavigate-9J1ZzN1J.js → useNavigate-BblEmQ3O.js} +1 -1
- package/dist/viewer/assets/{useNavigate-V8wjv5GQ.js → useNavigate-Ci7hClRo.js} +1 -1
- package/dist/viewer/assets/useQuery-5OkVbV1s.js +1 -0
- package/dist/viewer/assets/useQuery-B3Ek2Yet.js +1 -0
- package/dist/viewer/assets/{validIcons-UmfwFGTT.js → validIcons-BzGQ4tWD.js} +1 -1
- package/dist/viewer/assets/{validIcons-Dqgxgpv-.js → validIcons-CzxkK9y8.js} +1 -1
- package/dist/viewer/index.html +12 -10
- package/package.json +3 -6
- package/dist/viewer/assets/AppDetailPage-BPvWscWJ.js +0 -1
- package/dist/viewer/assets/AppDetailPage-DIuYKCTQ.js +0 -1
- package/dist/viewer/assets/AppDetailPage-r3jzO9Kf.js +0 -1
- package/dist/viewer/assets/BootstrapContext-CExPp3R2.js +0 -1
- package/dist/viewer/assets/BootstrapContext-CTQMWXf-.js +0 -1
- package/dist/viewer/assets/BootstrapContext-DbnbWmQT.js +0 -1
- package/dist/viewer/assets/Card-Bmn1HMWl.js +0 -1
- package/dist/viewer/assets/Card-DMz6T3VL.js +0 -1
- package/dist/viewer/assets/Card-DsqKctyM.js +0 -1
- package/dist/viewer/assets/CodeStepsPage-BHqLfCdV.js +0 -4
- package/dist/viewer/assets/CodeStepsPage-BymhEfzO.js +0 -4
- package/dist/viewer/assets/CodeStepsPage-CD_MhDwY.js +0 -4
- package/dist/viewer/assets/CodeStepsPage-CgI-LqEx.js +0 -4
- package/dist/viewer/assets/CodeStepsPage-Dak3saZE.js +0 -5
- package/dist/viewer/assets/CodeStepsPage-Zj6DscGd.js +0 -4
- package/dist/viewer/assets/ConfigurationPage-Cjy9TH3N.js +0 -1
- package/dist/viewer/assets/ConfigurationPage-DmX-6MM-.js +0 -1
- package/dist/viewer/assets/ConfigurationPage-J56kCD_e.js +0 -1
- package/dist/viewer/assets/CredentialsContext-Xbse1T0q.js +0 -1
- package/dist/viewer/assets/CredentialsContext-zl2e9kpM.js +0 -1
- package/dist/viewer/assets/DevSidebar-B30mgxSn.js +0 -1
- package/dist/viewer/assets/DevSidebar-CsEdCTej.js +0 -1
- package/dist/viewer/assets/IconReferencePage-B6j9wlte.js +0 -1
- package/dist/viewer/assets/IconReferencePage-CJa9yg9Z.js +0 -1
- package/dist/viewer/assets/IconReferencePage-Q5I-lMFQ.js +0 -1
- package/dist/viewer/assets/SandboxPage-C0h7GUkw.js +0 -1
- package/dist/viewer/assets/SandboxPage-GDcydJnJ.js +0 -1
- package/dist/viewer/assets/SandboxPage-JREBQ4id.js +0 -1
- package/dist/viewer/assets/SidebarContext-BPkYyxET.js +0 -1
- package/dist/viewer/assets/SidebarContext-DVDXFWJI.js +0 -1
- package/dist/viewer/assets/SidebarContext-HucmR353.js +0 -1
- package/dist/viewer/assets/VersionsPage-B76eugMm.js +0 -1
- package/dist/viewer/assets/VersionsPage-BIpB0iBz.js +0 -1
- package/dist/viewer/assets/VersionsPage-D0qenxK5.js +0 -1
- package/dist/viewer/assets/bundleQuery-206rQR6g.js +0 -1
- package/dist/viewer/assets/bundleQuery-L4H4wq5q.js +0 -1
- package/dist/viewer/assets/fa-core-DaD7O-yO.js +0 -558
- package/dist/viewer/assets/index-BPiwld5D.js +0 -5
- package/dist/viewer/assets/index-BiyeMU_r.css +0 -2
- package/dist/viewer/assets/index-BlJv_543.js +0 -5
- package/dist/viewer/assets/index-CAYNux5J.js +0 -5
- package/dist/viewer/assets/index-D4AnoAXz.js +0 -5
- package/dist/viewer/assets/index-D8Koqlb-.js +0 -5
- package/dist/viewer/assets/index-DKrC0qkC.js +0 -5
- package/dist/viewer/assets/useLocalStorage-CQO0VGQ6.js +0 -1
- package/dist/viewer/assets/useObjectLookups-BAqe9wvu.js +0 -14
- package/dist/viewer/assets/useObjectLookups-C65zm-bM.js +0 -14
- package/dist/viewer/assets/useObjectLookups-CslM2vSA.js +0 -14
- /package/dist/viewer/assets/{CodeStepsPage-DdKuH37F.css → CodeViewer-DdKuH37F.css} +0 -0
- /package/dist/viewer/assets/{fa-pro-icons-CiTC899m.js → fa-pro-icons-Dr3O0gTt.js} +0 -0
- /package/dist/viewer/assets/{fa-pro-icons-Dzkwxnip.js → fa-pro-icons-j8-_MCQ7.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -89,7 +89,8 @@ async function runBuild(pluginDir, outputDir, onStep) {
|
|
|
89
89
|
return serializePlugin(plugin, allReleaseNotes);
|
|
90
90
|
});
|
|
91
91
|
const jsonStr = JSON.stringify(bundle, null, 2);
|
|
92
|
-
const
|
|
92
|
+
const sizedBundle = bundle.map(({ allReleaseNotes: _notes, ...rest }) => rest);
|
|
93
|
+
const bundleSize = Buffer.byteLength(JSON.stringify(sizedBundle, null, 2), "utf-8");
|
|
93
94
|
await writeFile(join2(outputDir, "bundle.json"), jsonStr, "utf-8");
|
|
94
95
|
return { bundleSize };
|
|
95
96
|
}
|
|
@@ -220,7 +221,7 @@ var BuildUI = ({ outputDir, pluginDir }) => {
|
|
|
220
221
|
] }) }, s);
|
|
221
222
|
}),
|
|
222
223
|
isDone && bundleSize !== null && /* @__PURE__ */ jsx4(Box3, { marginTop: 1, gap: 1, children: /* @__PURE__ */ jsxs2(Text4, { color: "green", children: [
|
|
223
|
-
"\u2713 bundle
|
|
224
|
+
"\u2713 bundle \u2014 ",
|
|
224
225
|
formatBytes(bundleSize)
|
|
225
226
|
] }) }),
|
|
226
227
|
errorMessage !== null && /* @__PURE__ */ jsx4(Box3, { marginTop: 1, borderStyle: "single", borderColor: "red", paddingX: 1, children: /* @__PURE__ */ jsx4(Text4, { color: "red", children: errorMessage }) })
|
|
@@ -257,95 +258,418 @@ function buildCommand(program2) {
|
|
|
257
258
|
});
|
|
258
259
|
}
|
|
259
260
|
|
|
260
|
-
// src/commands/
|
|
261
|
+
// src/commands/create.ts
|
|
261
262
|
import { createElement as createElement2 } from "react";
|
|
262
263
|
import { render as render2 } from "ink";
|
|
263
264
|
|
|
264
|
-
// src/ui/
|
|
265
|
-
import { useCallback
|
|
266
|
-
import { Box as
|
|
267
|
-
import {
|
|
268
|
-
import { createServer } from "http";
|
|
269
|
-
import { join as join9 } from "path";
|
|
270
|
-
import { WebSocketServer } from "ws";
|
|
271
|
-
|
|
272
|
-
// src/server/requestHandler.ts
|
|
273
|
-
import { createReadStream } from "fs";
|
|
274
|
-
import { access as access2, readFile as readFile4 } from "fs/promises";
|
|
275
|
-
import { dirname as dirname2, extname, join as join7 } from "path";
|
|
276
|
-
import { fileURLToPath } from "url";
|
|
265
|
+
// src/ui/CreateUI.tsx
|
|
266
|
+
import { useCallback, useState as useState3 } from "react";
|
|
267
|
+
import { Box as Box4, Text as Text5, useApp as useApp2, useInput } from "ink";
|
|
268
|
+
import { join as join5 } from "path";
|
|
277
269
|
|
|
278
|
-
// src/lib/
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
270
|
+
// src/lib/createPlugin.ts
|
|
271
|
+
import { access, mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
272
|
+
import { join as join4 } from "path";
|
|
273
|
+
async function pathExists(path2) {
|
|
274
|
+
try {
|
|
275
|
+
await access(path2);
|
|
276
|
+
return true;
|
|
277
|
+
} catch {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async function precheckTargetDir(dir) {
|
|
282
|
+
if (await pathExists(join4(dir, "kizen.json"))) {
|
|
283
|
+
return "has-manifest";
|
|
284
|
+
}
|
|
285
|
+
if (await pathExists(join4(dir, ".kizenapp"))) {
|
|
286
|
+
return "has-kizenapp";
|
|
287
|
+
}
|
|
288
|
+
return "ok";
|
|
289
|
+
}
|
|
290
|
+
async function findAvailableSubDir(parentDir, baseName) {
|
|
291
|
+
const base = join4(parentDir, baseName);
|
|
292
|
+
if (!await pathExists(base)) {
|
|
293
|
+
return base;
|
|
294
|
+
}
|
|
295
|
+
for (let i = 1; i < 1e3; i++) {
|
|
296
|
+
const candidate = join4(parentDir, `${baseName}-${String(i)}`);
|
|
297
|
+
if (!await pathExists(candidate)) {
|
|
298
|
+
return candidate;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
throw new Error(`Could not find an available directory name for "${baseName}"`);
|
|
302
|
+
}
|
|
303
|
+
function buildManifest(input) {
|
|
304
|
+
return {
|
|
305
|
+
name: input.name,
|
|
306
|
+
version: "1.0.0",
|
|
307
|
+
published: true,
|
|
308
|
+
api_name: input.apiName,
|
|
309
|
+
external_link: input.externalLink,
|
|
310
|
+
description: input.description,
|
|
311
|
+
entry: "src/",
|
|
312
|
+
engine: "1.0.0",
|
|
313
|
+
release_notes_directory: "releaseNotes/",
|
|
314
|
+
release_environments: ["prod"],
|
|
315
|
+
config_template: {},
|
|
316
|
+
base_config: {},
|
|
317
|
+
developer_business_id: input.developerBusinessId
|
|
284
318
|
};
|
|
319
|
+
}
|
|
320
|
+
async function createPlugin(input) {
|
|
321
|
+
await mkdir2(input.targetDir, { recursive: true });
|
|
322
|
+
const manifest = buildManifest(input);
|
|
323
|
+
await writeFile2(
|
|
324
|
+
join4(input.targetDir, "kizen.json"),
|
|
325
|
+
JSON.stringify(manifest, null, 2) + "\n",
|
|
326
|
+
"utf-8"
|
|
327
|
+
);
|
|
328
|
+
await mkdir2(join4(input.targetDir, "src"), { recursive: true });
|
|
329
|
+
await mkdir2(join4(input.targetDir, "releaseNotes"), { recursive: true });
|
|
330
|
+
ensureGitignore(input.targetDir);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/ui/CreateUI.tsx
|
|
334
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
335
|
+
var FIELDS = ["name", "apiName", "externalLink", "description", "developerBusinessId"];
|
|
336
|
+
var FIELD_LABELS = {
|
|
337
|
+
name: "Name",
|
|
338
|
+
apiName: "API name",
|
|
339
|
+
externalLink: "External link",
|
|
340
|
+
description: "Description",
|
|
341
|
+
developerBusinessId: "Business ID"
|
|
342
|
+
};
|
|
343
|
+
var REQUIRED_FIELDS = ["name", "apiName"];
|
|
344
|
+
function inferApiName(name) {
|
|
345
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
346
|
+
}
|
|
347
|
+
var Hint = ({ text }) => /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: text });
|
|
348
|
+
function emptyValues(defaultBusinessId) {
|
|
285
349
|
return {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
350
|
+
name: "",
|
|
351
|
+
apiName: "",
|
|
352
|
+
externalLink: "",
|
|
353
|
+
description: "",
|
|
354
|
+
developerBusinessId: defaultBusinessId
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
var CreateUI = ({ parentDir, defaultBusinessId }) => {
|
|
358
|
+
const app = useApp2();
|
|
359
|
+
const [phase, setPhase] = useState3({ type: "target-select", cursor: 0 });
|
|
360
|
+
const [mode, setMode] = useState3("in-place");
|
|
361
|
+
const [values, setValues] = useState3(() => emptyValues(defaultBusinessId));
|
|
362
|
+
const [inputBuffer, setInputBuffer] = useState3("");
|
|
363
|
+
const [targetDir, setTargetDir] = useState3(parentDir);
|
|
364
|
+
const enterFieldEntry = useCallback((nextMode, initialValues) => {
|
|
365
|
+
setMode(nextMode);
|
|
366
|
+
setInputBuffer(initialValues.name);
|
|
367
|
+
setPhase({ type: "field-entry", field: 0, values: initialValues });
|
|
368
|
+
}, []);
|
|
369
|
+
const runPrecheck = useCallback(
|
|
370
|
+
async (dir) => {
|
|
371
|
+
const result = await precheckTargetDir(dir);
|
|
372
|
+
if (result !== "ok") {
|
|
373
|
+
setPhase({ type: "precheck-error", targetDir: dir, reason: result });
|
|
374
|
+
return;
|
|
290
375
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
376
|
+
setTargetDir(dir);
|
|
377
|
+
enterFieldEntry("in-place", values);
|
|
378
|
+
},
|
|
379
|
+
[values, enterFieldEntry]
|
|
380
|
+
);
|
|
381
|
+
const resolveSubDirAndConfirm = useCallback(
|
|
382
|
+
async (finalValues) => {
|
|
383
|
+
try {
|
|
384
|
+
const dir = await findAvailableSubDir(parentDir, finalValues.apiName);
|
|
385
|
+
setTargetDir(dir);
|
|
386
|
+
setPhase({ type: "confirm", values: finalValues });
|
|
387
|
+
} catch (err) {
|
|
388
|
+
setPhase({
|
|
389
|
+
type: "error",
|
|
390
|
+
message: err instanceof Error ? err.message : String(err)
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
[parentDir]
|
|
395
|
+
);
|
|
396
|
+
const runWrite = useCallback(async (finalValues, dir) => {
|
|
397
|
+
try {
|
|
398
|
+
await createPlugin({
|
|
399
|
+
targetDir: dir,
|
|
400
|
+
name: finalValues.name,
|
|
401
|
+
apiName: finalValues.apiName,
|
|
402
|
+
externalLink: finalValues.externalLink,
|
|
403
|
+
description: finalValues.description,
|
|
404
|
+
developerBusinessId: finalValues.developerBusinessId
|
|
304
405
|
});
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
emitChange();
|
|
311
|
-
}
|
|
312
|
-
return { response, fromCache: false };
|
|
406
|
+
setPhase({ type: "done", targetDir: dir });
|
|
407
|
+
} catch (err) {
|
|
408
|
+
setPhase({
|
|
409
|
+
type: "error",
|
|
410
|
+
message: err instanceof Error ? err.message : String(err)
|
|
313
411
|
});
|
|
314
|
-
},
|
|
315
|
-
clear() {
|
|
316
|
-
cache.clear();
|
|
317
|
-
emitChange();
|
|
318
412
|
}
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
413
|
+
}, []);
|
|
414
|
+
useInput((input, key) => {
|
|
415
|
+
if (key.ctrl && input === "c") {
|
|
416
|
+
app.exit();
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (phase.type === "target-select") {
|
|
420
|
+
if (key.upArrow) {
|
|
421
|
+
setPhase({ type: "target-select", cursor: 0 });
|
|
422
|
+
} else if (key.downArrow) {
|
|
423
|
+
setPhase({ type: "target-select", cursor: 1 });
|
|
424
|
+
} else if (key.return) {
|
|
425
|
+
if (phase.cursor === 0) {
|
|
426
|
+
void runPrecheck(parentDir);
|
|
427
|
+
} else {
|
|
428
|
+
enterFieldEntry("sub-dir", values);
|
|
429
|
+
}
|
|
430
|
+
} else if (key.escape) {
|
|
431
|
+
app.exit();
|
|
432
|
+
}
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (phase.type === "precheck-error") {
|
|
436
|
+
if (key.return || key.escape) {
|
|
437
|
+
app.exit();
|
|
438
|
+
}
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (phase.type === "field-entry") {
|
|
442
|
+
const { field, values: current } = phase;
|
|
443
|
+
const fieldName = FIELDS[field];
|
|
444
|
+
if (!fieldName) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (key.escape) {
|
|
448
|
+
app.exit();
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (key.backspace || key.delete) {
|
|
452
|
+
setInputBuffer((prev) => prev.slice(0, -1));
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (key.upArrow && field > 0) {
|
|
456
|
+
const updated = { ...current, [fieldName]: inputBuffer };
|
|
457
|
+
const prevField = field - 1;
|
|
458
|
+
const prevName = FIELDS[prevField];
|
|
459
|
+
setPhase({ type: "field-entry", field: prevField, values: updated });
|
|
460
|
+
setValues(updated);
|
|
461
|
+
setInputBuffer(prevName ? updated[prevName] : "");
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (key.return || key.tab || key.downArrow) {
|
|
465
|
+
const buffer = inputBuffer.trim();
|
|
466
|
+
const isRequired = REQUIRED_FIELDS.includes(fieldName);
|
|
467
|
+
if (isRequired && !buffer) {
|
|
468
|
+
setPhase({
|
|
469
|
+
type: "field-entry",
|
|
470
|
+
field,
|
|
471
|
+
values: current,
|
|
472
|
+
fieldError: `${FIELD_LABELS[fieldName]} is required`
|
|
473
|
+
});
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const valueToStore = fieldName === "description" ? inputBuffer : buffer;
|
|
477
|
+
const updated = { ...current, [fieldName]: valueToStore };
|
|
478
|
+
setValues(updated);
|
|
479
|
+
const nextField = field + 1;
|
|
480
|
+
if (nextField >= FIELDS.length) {
|
|
481
|
+
setInputBuffer("");
|
|
482
|
+
if (mode === "sub-dir") {
|
|
483
|
+
setPhase({ type: "resolving-target" });
|
|
484
|
+
void resolveSubDirAndConfirm(updated);
|
|
485
|
+
} else {
|
|
486
|
+
setPhase({ type: "confirm", values: updated });
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
const nextName = FIELDS[nextField];
|
|
490
|
+
const existing = nextName ? updated[nextName] : "";
|
|
491
|
+
const nextBuffer = nextName === "apiName" && !existing ? inferApiName(updated.name) : existing;
|
|
492
|
+
setPhase({ type: "field-entry", field: nextField, values: updated });
|
|
493
|
+
setInputBuffer(nextBuffer);
|
|
494
|
+
}
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
if (input && !key.ctrl && !key.meta) {
|
|
498
|
+
setInputBuffer((prev) => prev + input);
|
|
499
|
+
}
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (phase.type === "confirm") {
|
|
503
|
+
if (key.escape) {
|
|
504
|
+
const lastField = FIELDS.length - 1;
|
|
505
|
+
const lastName = FIELDS[lastField];
|
|
506
|
+
setPhase({ type: "field-entry", field: lastField, values: phase.values });
|
|
507
|
+
setInputBuffer(lastName ? phase.values[lastName] : "");
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (key.return) {
|
|
511
|
+
setPhase({ type: "writing" });
|
|
512
|
+
void runWrite(phase.values, targetDir);
|
|
513
|
+
}
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (phase.type === "done" || phase.type === "error") {
|
|
517
|
+
if (key.return || key.escape) {
|
|
518
|
+
app.exit();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
if (phase.type === "target-select") {
|
|
523
|
+
const options = [
|
|
524
|
+
{ label: "Use current directory", desc: parentDir },
|
|
525
|
+
{ label: "Create new sub-directory", desc: `${parentDir}/<inferred from API name>` }
|
|
526
|
+
];
|
|
527
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
528
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
529
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
530
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Where should the new plugin live?" }),
|
|
531
|
+
options.map((opt, i) => {
|
|
532
|
+
const selected = phase.cursor === i;
|
|
533
|
+
return /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
|
|
534
|
+
/* @__PURE__ */ jsx5(Text5, { ...selected && { color: "cyan" }, children: selected ? "\u276F" : " " }),
|
|
535
|
+
/* @__PURE__ */ jsx5(Text5, { bold: selected, ...selected && { color: "cyan" }, children: opt.label }),
|
|
536
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: opt.desc })
|
|
537
|
+
] }, opt.label);
|
|
538
|
+
})
|
|
539
|
+
] }),
|
|
540
|
+
/* @__PURE__ */ jsx5(Hint, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel" })
|
|
541
|
+
] });
|
|
542
|
+
}
|
|
543
|
+
if (phase.type === "precheck-error") {
|
|
544
|
+
const { targetDir: dir, reason } = phase;
|
|
545
|
+
const conflict = reason === "has-manifest" ? "kizen.json" : ".kizenapp/";
|
|
546
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
547
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
548
|
+
/* @__PURE__ */ jsx5(Text5, { color: "red", bold: true, children: "Cannot create plugin" }),
|
|
549
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
550
|
+
/* @__PURE__ */ jsxs3(Text5, { children: [
|
|
551
|
+
dir,
|
|
552
|
+
" already contains ",
|
|
553
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: conflict }),
|
|
554
|
+
"."
|
|
555
|
+
] }),
|
|
556
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Remove the conflicting file/directory or choose a different target to avoid overwriting an existing plugin." })
|
|
557
|
+
] }),
|
|
558
|
+
/* @__PURE__ */ jsx5(Hint, { text: "Enter or Esc to exit" })
|
|
559
|
+
] });
|
|
560
|
+
}
|
|
561
|
+
if (phase.type === "field-entry") {
|
|
562
|
+
const { field: activeField, fieldError } = phase;
|
|
563
|
+
const targetLine = mode === "sub-dir" ? `Target: sub-directory of ${parentDir} (inferred from API name)` : `Target: ${targetDir}`;
|
|
564
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
565
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
566
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
567
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Plugin details" }),
|
|
568
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: targetLine })
|
|
569
|
+
] }),
|
|
570
|
+
fieldError !== void 0 && /* @__PURE__ */ jsxs3(Text5, { color: "red", children: [
|
|
571
|
+
"Error: ",
|
|
572
|
+
fieldError
|
|
573
|
+
] }),
|
|
574
|
+
/* @__PURE__ */ jsx5(Box4, { flexDirection: "column", gap: 0, children: FIELDS.map((name, i) => {
|
|
575
|
+
const isActive = activeField === i;
|
|
576
|
+
const displayValue = isActive ? inputBuffer : values[name];
|
|
577
|
+
const isRequired = REQUIRED_FIELDS.includes(name);
|
|
578
|
+
const label = `${FIELD_LABELS[name]}${isRequired ? "" : " (optional)"}`;
|
|
579
|
+
return /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
|
|
580
|
+
/* @__PURE__ */ jsx5(Box4, { width: 24, children: /* @__PURE__ */ jsx5(Text5, { bold: isActive, ...isActive && { color: "cyan" }, children: label }) }),
|
|
581
|
+
/* @__PURE__ */ jsx5(Text5, { ...isActive && { color: "cyan" }, children: ">" }),
|
|
582
|
+
/* @__PURE__ */ jsx5(Text5, { children: displayValue }),
|
|
583
|
+
isActive && /* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u2588" })
|
|
584
|
+
] }, name);
|
|
585
|
+
}) }),
|
|
586
|
+
/* @__PURE__ */ jsx5(Hint, { text: "Enter/Tab/\u2193 next \xB7 \u2191 previous \xB7 Backspace delete \xB7 Esc cancel" })
|
|
587
|
+
] });
|
|
588
|
+
}
|
|
589
|
+
if (phase.type === "confirm") {
|
|
590
|
+
const manifest = buildManifest({
|
|
591
|
+
targetDir,
|
|
592
|
+
name: phase.values.name,
|
|
593
|
+
apiName: phase.values.apiName,
|
|
594
|
+
externalLink: phase.values.externalLink,
|
|
595
|
+
description: phase.values.description,
|
|
596
|
+
developerBusinessId: phase.values.developerBusinessId
|
|
597
|
+
});
|
|
598
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
599
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
600
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
601
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Ready to create" }),
|
|
602
|
+
/* @__PURE__ */ jsxs3(Text5, { dimColor: true, children: [
|
|
603
|
+
"Target: ",
|
|
604
|
+
targetDir
|
|
605
|
+
] })
|
|
606
|
+
] }),
|
|
607
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
608
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "kizen.json preview" }),
|
|
609
|
+
/* @__PURE__ */ jsx5(Text5, { children: JSON.stringify(manifest, null, 2) })
|
|
610
|
+
] }),
|
|
611
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
612
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Also creates:" }),
|
|
613
|
+
/* @__PURE__ */ jsxs3(Text5, { dimColor: true, children: [
|
|
614
|
+
" ",
|
|
615
|
+
join5(targetDir, "src/")
|
|
616
|
+
] }),
|
|
617
|
+
/* @__PURE__ */ jsxs3(Text5, { dimColor: true, children: [
|
|
618
|
+
" ",
|
|
619
|
+
join5(targetDir, "releaseNotes/")
|
|
620
|
+
] }),
|
|
621
|
+
/* @__PURE__ */ jsxs3(Text5, { dimColor: true, children: [
|
|
622
|
+
" ",
|
|
623
|
+
join5(targetDir, ".gitignore"),
|
|
624
|
+
" (adds .kizenapp/)"
|
|
625
|
+
] })
|
|
626
|
+
] }),
|
|
627
|
+
/* @__PURE__ */ jsx5(Hint, { text: "Enter to create \xB7 Esc to go back" })
|
|
628
|
+
] });
|
|
629
|
+
}
|
|
630
|
+
if (phase.type === "resolving-target") {
|
|
631
|
+
return /* @__PURE__ */ jsx5(Box4, { paddingY: 1, paddingX: 2, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Finding available sub-directory\u2026" }) });
|
|
632
|
+
}
|
|
633
|
+
if (phase.type === "writing") {
|
|
634
|
+
return /* @__PURE__ */ jsx5(Box4, { paddingY: 1, paddingX: 2, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Creating plugin\u2026" }) });
|
|
635
|
+
}
|
|
636
|
+
if (phase.type === "done") {
|
|
637
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
638
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
639
|
+
/* @__PURE__ */ jsxs3(Text5, { color: "green", bold: true, children: [
|
|
640
|
+
"Plugin created at ",
|
|
641
|
+
phase.targetDir
|
|
642
|
+
] }),
|
|
643
|
+
/* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", gap: 0, children: [
|
|
644
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Next steps:" }),
|
|
645
|
+
/* @__PURE__ */ jsxs3(Text5, { children: [
|
|
646
|
+
" ",
|
|
647
|
+
"cd ",
|
|
648
|
+
phase.targetDir
|
|
649
|
+
] }),
|
|
650
|
+
/* @__PURE__ */ jsxs3(Text5, { children: [
|
|
651
|
+
" ",
|
|
652
|
+
"appbuilder dev"
|
|
653
|
+
] })
|
|
654
|
+
] }),
|
|
655
|
+
/* @__PURE__ */ jsx5(Hint, { text: "Enter or Esc to exit" })
|
|
656
|
+
] });
|
|
657
|
+
}
|
|
658
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
659
|
+
/* @__PURE__ */ jsx5(AppHeader, {}),
|
|
660
|
+
/* @__PURE__ */ jsx5(Text5, { color: "red", bold: true, children: "Failed to create plugin" }),
|
|
661
|
+
/* @__PURE__ */ jsx5(Text5, { children: phase.message }),
|
|
662
|
+
/* @__PURE__ */ jsx5(Hint, { text: "Enter or Esc to exit" })
|
|
663
|
+
] });
|
|
340
664
|
};
|
|
341
665
|
|
|
342
666
|
// src/lib/credentials.ts
|
|
343
|
-
import { mkdir as
|
|
667
|
+
import { mkdir as mkdir3, readFile as readFile2, readdir as readdir2, writeFile as writeFile3 } from "fs/promises";
|
|
344
668
|
import { homedir } from "os";
|
|
345
|
-
import { dirname, join as
|
|
669
|
+
import { dirname, join as join6 } from "path";
|
|
346
670
|
var ENVIRONMENTS = ["go", "fmo", "staging", "integration", "test1"];
|
|
347
|
-
var GLOBAL_CREDENTIALS_DIR =
|
|
348
|
-
var GLOBAL_CREDENTIALS_PATH =
|
|
671
|
+
var GLOBAL_CREDENTIALS_DIR = join6(homedir(), ".kizenappbuilder");
|
|
672
|
+
var GLOBAL_CREDENTIALS_PATH = join6(GLOBAL_CREDENTIALS_DIR, "credentials.json");
|
|
349
673
|
var DEFAULT_PROFILE_NAME = "credentials";
|
|
350
674
|
function isValidEnvironment(value) {
|
|
351
675
|
return ENVIRONMENTS.includes(value);
|
|
@@ -375,11 +699,11 @@ async function loadGlobalCredentials() {
|
|
|
375
699
|
}
|
|
376
700
|
}
|
|
377
701
|
async function saveGlobalCredentials(credentials) {
|
|
378
|
-
await
|
|
379
|
-
await
|
|
702
|
+
await mkdir3(dirname(GLOBAL_CREDENTIALS_PATH), { recursive: true });
|
|
703
|
+
await writeFile3(GLOBAL_CREDENTIALS_PATH, JSON.stringify(credentials, null, 2), "utf-8");
|
|
380
704
|
}
|
|
381
705
|
function getProfilePath(name) {
|
|
382
|
-
return
|
|
706
|
+
return join6(GLOBAL_CREDENTIALS_DIR, `${name}.json`);
|
|
383
707
|
}
|
|
384
708
|
async function listCredentialProfiles() {
|
|
385
709
|
const profiles = [
|
|
@@ -395,7 +719,7 @@ async function listCredentialProfiles() {
|
|
|
395
719
|
if (name === DEFAULT_PROFILE_NAME) {
|
|
396
720
|
continue;
|
|
397
721
|
}
|
|
398
|
-
profiles.push({ name, path:
|
|
722
|
+
profiles.push({ name, path: join6(GLOBAL_CREDENTIALS_DIR, entry), isDefault: false });
|
|
399
723
|
}
|
|
400
724
|
} catch {
|
|
401
725
|
}
|
|
@@ -406,8 +730,8 @@ async function saveCredentialProfile(name, credentials) {
|
|
|
406
730
|
await saveGlobalCredentials(credentials);
|
|
407
731
|
return;
|
|
408
732
|
}
|
|
409
|
-
await
|
|
410
|
-
await
|
|
733
|
+
await mkdir3(GLOBAL_CREDENTIALS_DIR, { recursive: true });
|
|
734
|
+
await writeFile3(getProfilePath(name), JSON.stringify(credentials, null, 2), "utf-8");
|
|
411
735
|
}
|
|
412
736
|
async function loadCredentialProfile(name) {
|
|
413
737
|
try {
|
|
@@ -417,26 +741,121 @@ async function loadCredentialProfile(name) {
|
|
|
417
741
|
}
|
|
418
742
|
}
|
|
419
743
|
|
|
744
|
+
// src/commands/create.ts
|
|
745
|
+
function createCommand(program2) {
|
|
746
|
+
program2.command("create").description("Scaffold a new Kizen plugin project").action(async () => {
|
|
747
|
+
const parentDir = process.cwd();
|
|
748
|
+
const globalCreds = await loadGlobalCredentials();
|
|
749
|
+
const defaultBusinessId = globalCreds?.businessId ?? "";
|
|
750
|
+
const { waitUntilExit } = render2(createElement2(CreateUI, { parentDir, defaultBusinessId }), {
|
|
751
|
+
exitOnCtrlC: false
|
|
752
|
+
});
|
|
753
|
+
await waitUntilExit();
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/commands/dev.ts
|
|
758
|
+
import { createElement as createElement3 } from "react";
|
|
759
|
+
import { render as render3 } from "ink";
|
|
760
|
+
|
|
761
|
+
// src/ui/DevUI.tsx
|
|
762
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useRef, useState as useState6 } from "react";
|
|
763
|
+
import { Box as Box6, Text as Text7, useInput as useInput3 } from "ink";
|
|
764
|
+
import { watch } from "fs";
|
|
765
|
+
import { createServer } from "http";
|
|
766
|
+
import { join as join11 } from "path";
|
|
767
|
+
import { WebSocketServer } from "ws";
|
|
768
|
+
|
|
769
|
+
// src/server/requestHandler.ts
|
|
770
|
+
import { createReadStream } from "fs";
|
|
771
|
+
import { access as access3, readdir as readdir3, readFile as readFile4, stat } from "fs/promises";
|
|
772
|
+
import { dirname as dirname2, extname, join as join9, resolve, sep } from "path";
|
|
773
|
+
import { fileURLToPath } from "url";
|
|
774
|
+
|
|
775
|
+
// src/lib/proxyCache.ts
|
|
776
|
+
function createProxyCache(options = {}) {
|
|
777
|
+
const cache = /* @__PURE__ */ new Map();
|
|
778
|
+
const { onChange } = options;
|
|
779
|
+
const emitChange = () => {
|
|
780
|
+
onChange?.(cache.size);
|
|
781
|
+
};
|
|
782
|
+
return {
|
|
783
|
+
get(key, fetcher) {
|
|
784
|
+
const existing = cache.get(key);
|
|
785
|
+
if (existing !== void 0) {
|
|
786
|
+
return existing.then((response) => ({ response, fromCache: true }));
|
|
787
|
+
}
|
|
788
|
+
const pending = fetcher().then(async (response) => {
|
|
789
|
+
const headers = Object.fromEntries(response.headers);
|
|
790
|
+
delete headers["content-encoding"];
|
|
791
|
+
delete headers["content-length"];
|
|
792
|
+
return {
|
|
793
|
+
status: response.status,
|
|
794
|
+
headers,
|
|
795
|
+
body: Buffer.from(await response.arrayBuffer())
|
|
796
|
+
};
|
|
797
|
+
}).catch((error) => {
|
|
798
|
+
cache.delete(key);
|
|
799
|
+
emitChange();
|
|
800
|
+
throw error;
|
|
801
|
+
});
|
|
802
|
+
cache.set(key, pending);
|
|
803
|
+
emitChange();
|
|
804
|
+
return pending.then((response) => {
|
|
805
|
+
if (response.status >= 400) {
|
|
806
|
+
cache.delete(key);
|
|
807
|
+
emitChange();
|
|
808
|
+
}
|
|
809
|
+
return { response, fromCache: false };
|
|
810
|
+
});
|
|
811
|
+
},
|
|
812
|
+
clear() {
|
|
813
|
+
cache.clear();
|
|
814
|
+
emitChange();
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/lib/constants.ts
|
|
820
|
+
var LOG_LIMIT = 50;
|
|
821
|
+
var LOG_DISPLAY = 8;
|
|
822
|
+
var FILE_WATCH_DEBOUNCE_MS = 150;
|
|
823
|
+
var MIME_TYPES = {
|
|
824
|
+
".html": "text/html; charset=utf-8",
|
|
825
|
+
".js": "application/javascript; charset=utf-8",
|
|
826
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
827
|
+
".css": "text/css; charset=utf-8",
|
|
828
|
+
".png": "image/png",
|
|
829
|
+
".jpg": "image/jpeg",
|
|
830
|
+
".svg": "image/svg+xml",
|
|
831
|
+
".ico": "image/x-icon",
|
|
832
|
+
".json": "application/json; charset=utf-8",
|
|
833
|
+
".woff": "font/woff",
|
|
834
|
+
".woff2": "font/woff2",
|
|
835
|
+
".ttf": "font/ttf",
|
|
836
|
+
".map": "application/json"
|
|
837
|
+
};
|
|
838
|
+
|
|
420
839
|
// src/lib/config.ts
|
|
421
|
-
import { mkdir as
|
|
422
|
-
import { join as
|
|
840
|
+
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
841
|
+
import { join as join7 } from "path";
|
|
423
842
|
async function loadConfig(outputDir) {
|
|
424
843
|
try {
|
|
425
|
-
const content = await readFile3(
|
|
844
|
+
const content = await readFile3(join7(outputDir, "config.json"), "utf-8");
|
|
426
845
|
return JSON.parse(content);
|
|
427
846
|
} catch {
|
|
428
847
|
return {};
|
|
429
848
|
}
|
|
430
849
|
}
|
|
431
850
|
async function saveConfig(outputDir, config) {
|
|
432
|
-
await
|
|
433
|
-
await
|
|
851
|
+
await mkdir4(outputDir, { recursive: true });
|
|
852
|
+
await writeFile4(join7(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
|
|
434
853
|
}
|
|
435
854
|
|
|
436
855
|
// src/server/pythonExecutor.ts
|
|
437
|
-
import { spawn
|
|
438
|
-
import { writeFile as
|
|
439
|
-
import { join as
|
|
856
|
+
import { spawn } from "child_process";
|
|
857
|
+
import { writeFile as writeFile5, rm, mkdtemp, access as access2 } from "fs/promises";
|
|
858
|
+
import { join as join8 } from "path";
|
|
440
859
|
import { tmpdir } from "os";
|
|
441
860
|
var WRAPPER_SCRIPT = `
|
|
442
861
|
import sys, json, os, traceback
|
|
@@ -487,6 +906,80 @@ class _OutputsProxy:
|
|
|
487
906
|
|
|
488
907
|
outputs = _OutputsProxy()
|
|
489
908
|
|
|
909
|
+
# --- HTTP request capture (requests library only) ---
|
|
910
|
+
_kizen_http_requests = []
|
|
911
|
+
|
|
912
|
+
def _kizen_install_http_capture():
|
|
913
|
+
try:
|
|
914
|
+
import requests
|
|
915
|
+
except Exception:
|
|
916
|
+
return
|
|
917
|
+
import time, datetime
|
|
918
|
+
|
|
919
|
+
_original_send = requests.Session.send
|
|
920
|
+
|
|
921
|
+
def _kizen_send(self, request, **kwargs):
|
|
922
|
+
submitted_at = datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S')
|
|
923
|
+
start = time.time()
|
|
924
|
+
error_type = None
|
|
925
|
+
response = None
|
|
926
|
+
try:
|
|
927
|
+
response = _original_send(self, request, **kwargs)
|
|
928
|
+
return response
|
|
929
|
+
except Exception as exc:
|
|
930
|
+
error_type = type(exc).__name__
|
|
931
|
+
raise
|
|
932
|
+
finally:
|
|
933
|
+
body = request.body
|
|
934
|
+
if isinstance(body, bytes):
|
|
935
|
+
try:
|
|
936
|
+
body = body.decode('utf-8')
|
|
937
|
+
except Exception:
|
|
938
|
+
body = '<binary>'
|
|
939
|
+
elif body is None:
|
|
940
|
+
body = ''
|
|
941
|
+
|
|
942
|
+
status = 0
|
|
943
|
+
resp_headers = {}
|
|
944
|
+
resp_body = ''
|
|
945
|
+
if response is not None:
|
|
946
|
+
status = response.status_code
|
|
947
|
+
resp_headers = dict(response.headers)
|
|
948
|
+
if kwargs.get('stream'):
|
|
949
|
+
resp_body = '<streamed>'
|
|
950
|
+
else:
|
|
951
|
+
try:
|
|
952
|
+
resp_body = response.text
|
|
953
|
+
except Exception:
|
|
954
|
+
resp_body = '<unreadable>'
|
|
955
|
+
|
|
956
|
+
_kizen_http_requests.append({
|
|
957
|
+
'method': request.method or '',
|
|
958
|
+
'url': request.url or '',
|
|
959
|
+
'headers': dict(request.headers or {}),
|
|
960
|
+
'body': body,
|
|
961
|
+
'requestSubmittedAt': submitted_at,
|
|
962
|
+
'session': True,
|
|
963
|
+
'responseStatusCode': status,
|
|
964
|
+
'responseHeaders': resp_headers,
|
|
965
|
+
'responseBody': resp_body,
|
|
966
|
+
'requestErrorType': error_type,
|
|
967
|
+
'duration': time.time() - start,
|
|
968
|
+
})
|
|
969
|
+
|
|
970
|
+
requests.Session.send = _kizen_send
|
|
971
|
+
|
|
972
|
+
_kizen_install_http_capture()
|
|
973
|
+
|
|
974
|
+
def _kizen_http_summary():
|
|
975
|
+
if not _kizen_http_requests:
|
|
976
|
+
return None
|
|
977
|
+
return {
|
|
978
|
+
'count': len(_kizen_http_requests),
|
|
979
|
+
'notLogged': 0,
|
|
980
|
+
'requests': _kizen_http_requests,
|
|
981
|
+
}
|
|
982
|
+
|
|
490
983
|
# --- Execute user script ---
|
|
491
984
|
try:
|
|
492
985
|
exec(open(os.environ['__KIZEN_SCRIPT_PATH__'], encoding='utf-8').read())
|
|
@@ -495,6 +988,7 @@ try:
|
|
|
495
988
|
'outputValues': outputs._values,
|
|
496
989
|
'logs': outputs._logs,
|
|
497
990
|
'error': None,
|
|
991
|
+
'httpRequests': _kizen_http_summary(),
|
|
498
992
|
}
|
|
499
993
|
except Exception:
|
|
500
994
|
# Build a clean traceback that hides the wrapper internals.
|
|
@@ -514,6 +1008,7 @@ except Exception:
|
|
|
514
1008
|
'outputValues': outputs._values,
|
|
515
1009
|
'logs': outputs._logs,
|
|
516
1010
|
'error': ''.join(lines),
|
|
1011
|
+
'httpRequests': _kizen_http_summary(),
|
|
517
1012
|
}
|
|
518
1013
|
|
|
519
1014
|
# Write results between markers so we can extract them reliably
|
|
@@ -524,8 +1019,21 @@ sys.stdout.write('\\n__KIZEN_RESULT_END__\\n')
|
|
|
524
1019
|
sys.stdout.flush()
|
|
525
1020
|
`.trim();
|
|
526
1021
|
var DEFAULT_TIMEOUT = 3e4;
|
|
527
|
-
var VENV_DIR =
|
|
528
|
-
var VENV_PACKAGES = [
|
|
1022
|
+
var VENV_DIR = join8(process.cwd(), ".kizenapp", "venv");
|
|
1023
|
+
var VENV_PACKAGES = [
|
|
1024
|
+
"awslambdaric",
|
|
1025
|
+
"bcrypt",
|
|
1026
|
+
"fpdf2",
|
|
1027
|
+
"lxml",
|
|
1028
|
+
"msgpack",
|
|
1029
|
+
"numpy",
|
|
1030
|
+
"paramiko",
|
|
1031
|
+
"pyjwt",
|
|
1032
|
+
"pypdf",
|
|
1033
|
+
"pytz",
|
|
1034
|
+
"requests",
|
|
1035
|
+
"tzdata"
|
|
1036
|
+
];
|
|
529
1037
|
var venvReady = null;
|
|
530
1038
|
function resolvePythonBinary(runtime) {
|
|
531
1039
|
const match = /^python-(\d+\.\d+)$/.exec(runtime);
|
|
@@ -534,19 +1042,82 @@ function resolvePythonBinary(runtime) {
|
|
|
534
1042
|
}
|
|
535
1043
|
return "python3";
|
|
536
1044
|
}
|
|
537
|
-
function
|
|
1045
|
+
function runCommand(bin, args, onLine) {
|
|
1046
|
+
return new Promise((resolve2, reject) => {
|
|
1047
|
+
const proc = spawn(bin, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
1048
|
+
const pipeLines = (source, stream) => {
|
|
1049
|
+
const captured = [];
|
|
1050
|
+
let buffer = "";
|
|
1051
|
+
source.on("data", (chunk) => {
|
|
1052
|
+
buffer += chunk.toString("utf-8");
|
|
1053
|
+
let idx = buffer.indexOf("\n");
|
|
1054
|
+
while (idx >= 0) {
|
|
1055
|
+
const line = buffer.slice(0, idx).replace(/\r$/, "");
|
|
1056
|
+
buffer = buffer.slice(idx + 1);
|
|
1057
|
+
captured.push(line);
|
|
1058
|
+
onLine?.(line, stream);
|
|
1059
|
+
idx = buffer.indexOf("\n");
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
source.on("end", () => {
|
|
1063
|
+
if (buffer.length > 0) {
|
|
1064
|
+
const line = buffer.replace(/\r$/, "");
|
|
1065
|
+
captured.push(line);
|
|
1066
|
+
onLine?.(line, stream);
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
return captured;
|
|
1070
|
+
};
|
|
1071
|
+
const stderrCaptured = pipeLines(proc.stderr, "stderr");
|
|
1072
|
+
pipeLines(proc.stdout, "stdout");
|
|
1073
|
+
proc.on("error", reject);
|
|
1074
|
+
proc.on("close", (code) => {
|
|
1075
|
+
if (code === 0) {
|
|
1076
|
+
resolve2();
|
|
1077
|
+
} else {
|
|
1078
|
+
const tail = stderrCaptured.slice(-10).join("\n").trim();
|
|
1079
|
+
const detail = tail.length > 0 ? `: ${tail}` : "";
|
|
1080
|
+
reject(new Error(`${bin} exited with code ${String(code)}${detail}`));
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
var installListeners = /* @__PURE__ */ new Set();
|
|
1086
|
+
function emitInstall(event) {
|
|
1087
|
+
for (const listener of installListeners) {
|
|
1088
|
+
listener(event);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
function ensureVenv(runtime, listener) {
|
|
1092
|
+
if (listener) {
|
|
1093
|
+
installListeners.add(listener);
|
|
1094
|
+
}
|
|
538
1095
|
if (!venvReady) {
|
|
539
1096
|
venvReady = (async () => {
|
|
540
|
-
const venvPython =
|
|
1097
|
+
const venvPython = join8(VENV_DIR, "bin", "python");
|
|
541
1098
|
try {
|
|
542
|
-
await
|
|
1099
|
+
await access2(venvPython);
|
|
543
1100
|
} catch {
|
|
544
1101
|
const systemPython = resolvePythonBinary(runtime);
|
|
545
|
-
|
|
1102
|
+
await runCommand(systemPython, ["-m", "venv", VENV_DIR]);
|
|
1103
|
+
}
|
|
1104
|
+
emitInstall({ kind: "start" });
|
|
1105
|
+
try {
|
|
1106
|
+
await runCommand(
|
|
1107
|
+
venvPython,
|
|
1108
|
+
["-m", "pip", "install", "--disable-pip-version-check", ...VENV_PACKAGES],
|
|
1109
|
+
(line, stream) => {
|
|
1110
|
+
emitInstall({ kind: "log", line, stream });
|
|
1111
|
+
}
|
|
1112
|
+
);
|
|
1113
|
+
emitInstall({ kind: "complete" });
|
|
1114
|
+
} catch (err) {
|
|
1115
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1116
|
+
emitInstall({ kind: "error", message });
|
|
1117
|
+
throw err;
|
|
1118
|
+
} finally {
|
|
1119
|
+
installListeners.clear();
|
|
546
1120
|
}
|
|
547
|
-
execFileSync(venvPython, ["-m", "pip", "install", "-q", ...VENV_PACKAGES], {
|
|
548
|
-
stdio: "ignore"
|
|
549
|
-
});
|
|
550
1121
|
return venvPython;
|
|
551
1122
|
})();
|
|
552
1123
|
}
|
|
@@ -571,16 +1142,16 @@ function parseResult(stdout) {
|
|
|
571
1142
|
}
|
|
572
1143
|
async function executePythonStep(params) {
|
|
573
1144
|
const timeout = params.timeout ?? DEFAULT_TIMEOUT;
|
|
574
|
-
const pythonBin = await ensureVenv(params.scriptRuntime);
|
|
1145
|
+
const pythonBin = await ensureVenv(params.scriptRuntime, params.onInstallProgress);
|
|
575
1146
|
const start = Date.now();
|
|
576
1147
|
let tmpDir;
|
|
577
1148
|
try {
|
|
578
|
-
tmpDir = await mkdtemp(
|
|
579
|
-
const wrapperPath =
|
|
580
|
-
const scriptPath =
|
|
1149
|
+
tmpDir = await mkdtemp(join8(tmpdir(), "kizen-step-"));
|
|
1150
|
+
const wrapperPath = join8(tmpDir, "wrapper.py");
|
|
1151
|
+
const scriptPath = join8(tmpDir, "script.py");
|
|
581
1152
|
await Promise.all([
|
|
582
|
-
|
|
583
|
-
|
|
1153
|
+
writeFile5(wrapperPath, WRAPPER_SCRIPT, "utf-8"),
|
|
1154
|
+
writeFile5(scriptPath, params.script, "utf-8")
|
|
584
1155
|
]);
|
|
585
1156
|
const env = {
|
|
586
1157
|
...process.env,
|
|
@@ -588,7 +1159,7 @@ async function executePythonStep(params) {
|
|
|
588
1159
|
__KIZEN_SECRETS__: JSON.stringify(params.secrets),
|
|
589
1160
|
__KIZEN_SCRIPT_PATH__: scriptPath
|
|
590
1161
|
};
|
|
591
|
-
const { exitCode, stdout, stderr } = await new Promise((
|
|
1162
|
+
const { exitCode, stdout, stderr } = await new Promise((resolve2) => {
|
|
592
1163
|
const stdoutChunks = [];
|
|
593
1164
|
const stderrChunks = [];
|
|
594
1165
|
let settled = false;
|
|
@@ -612,7 +1183,7 @@ async function executePythonStep(params) {
|
|
|
612
1183
|
}
|
|
613
1184
|
settled = true;
|
|
614
1185
|
clearTimeout(timer);
|
|
615
|
-
|
|
1186
|
+
resolve2({
|
|
616
1187
|
exitCode: code ?? 1,
|
|
617
1188
|
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
618
1189
|
stderr: Buffer.concat(stderrChunks).toString("utf-8")
|
|
@@ -625,7 +1196,7 @@ async function executePythonStep(params) {
|
|
|
625
1196
|
}
|
|
626
1197
|
settled = true;
|
|
627
1198
|
clearTimeout(timer);
|
|
628
|
-
|
|
1199
|
+
resolve2({
|
|
629
1200
|
exitCode: 1,
|
|
630
1201
|
stdout: "",
|
|
631
1202
|
stderr: err.message
|
|
@@ -647,6 +1218,9 @@ async function executePythonStep(params) {
|
|
|
647
1218
|
if (result.error !== null) {
|
|
648
1219
|
base2.error = result.error;
|
|
649
1220
|
}
|
|
1221
|
+
if (result.httpRequests) {
|
|
1222
|
+
base2.httpRequests = result.httpRequests;
|
|
1223
|
+
}
|
|
650
1224
|
return base2;
|
|
651
1225
|
}
|
|
652
1226
|
const base = {
|
|
@@ -661,149 +1235,63 @@ async function executePythonStep(params) {
|
|
|
661
1235
|
if (exitCode !== 0) {
|
|
662
1236
|
base.error = `Process exited with code ${String(exitCode)}`;
|
|
663
1237
|
}
|
|
664
|
-
return base;
|
|
665
|
-
} finally {
|
|
666
|
-
if (tmpDir) {
|
|
667
|
-
void rm(tmpDir, { recursive: true, force: true });
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// src/server/remoteCodeRunner.ts
|
|
673
|
-
var BASE_URLS = {
|
|
674
|
-
go: "https://app.go.kizen.com/api",
|
|
675
|
-
fmo: "https://app.fmo.kizen.com/api",
|
|
676
|
-
staging: "https://staging.kizen.dev/api",
|
|
677
|
-
integration: "https://integration.kizen.dev/api",
|
|
678
|
-
test1: "https://test1.kizen.dev/api"
|
|
679
|
-
};
|
|
680
|
-
var SUPPORTED_RUNTIMES = ["python-3-13", "python-3-12"];
|
|
681
|
-
var DEFAULT_RUNTIME = "python-3-12";
|
|
682
|
-
var DATA_TYPE_TO_KIZEN = {
|
|
683
|
-
text: "s",
|
|
684
|
-
string: "s",
|
|
685
|
-
number: "n",
|
|
686
|
-
integer: "n",
|
|
687
|
-
boolean: "b",
|
|
688
|
-
date: "d",
|
|
689
|
-
datetime: "dt",
|
|
690
|
-
phone: "p",
|
|
691
|
-
phonenumber: "p",
|
|
692
|
-
uuid: "u",
|
|
693
|
-
file: "f",
|
|
694
|
-
list: "l"
|
|
695
|
-
};
|
|
696
|
-
function toKizenType(dataType) {
|
|
697
|
-
return DATA_TYPE_TO_KIZEN[dataType.toLowerCase()] ?? "s";
|
|
698
|
-
}
|
|
699
|
-
function resolveRuntime(runtime) {
|
|
700
|
-
if (SUPPORTED_RUNTIMES.includes(runtime)) {
|
|
701
|
-
return runtime;
|
|
702
|
-
}
|
|
703
|
-
return DEFAULT_RUNTIME;
|
|
704
|
-
}
|
|
705
|
-
function encodeInputs(inputs, inputTypes) {
|
|
706
|
-
const keys = Object.keys(inputs);
|
|
707
|
-
if (keys.length === 0) {
|
|
708
|
-
return null;
|
|
709
|
-
}
|
|
710
|
-
const encoded = {};
|
|
711
|
-
for (const key of keys) {
|
|
712
|
-
encoded[key] = {
|
|
713
|
-
t: toKizenType(inputTypes[key] ?? "string"),
|
|
714
|
-
v: inputs[key]
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
return encoded;
|
|
718
|
-
}
|
|
719
|
-
function encodeOutputTypes(outputTypes) {
|
|
720
|
-
const keys = Object.keys(outputTypes);
|
|
721
|
-
if (keys.length === 0) {
|
|
722
|
-
return null;
|
|
723
|
-
}
|
|
724
|
-
const encoded = {};
|
|
725
|
-
for (const key of keys) {
|
|
726
|
-
encoded[key] = toKizenType(outputTypes[key] ?? "string");
|
|
727
|
-
}
|
|
728
|
-
return encoded;
|
|
729
|
-
}
|
|
730
|
-
function decodeOutputValues(values) {
|
|
731
|
-
if (!values) {
|
|
732
|
-
return {};
|
|
733
|
-
}
|
|
734
|
-
const decoded = {};
|
|
735
|
-
for (const [key, encoded] of Object.entries(values)) {
|
|
736
|
-
decoded[key] = encoded.v;
|
|
737
|
-
}
|
|
738
|
-
return decoded;
|
|
739
|
-
}
|
|
740
|
-
async function executeRemoteStep(params, credentials) {
|
|
741
|
-
const baseUrl = BASE_URLS[credentials.environment];
|
|
742
|
-
const url = `${baseUrl}/coderunner/run`;
|
|
743
|
-
const start = Date.now();
|
|
744
|
-
const body = {
|
|
745
|
-
user_script: params.script,
|
|
746
|
-
runtime: resolveRuntime(params.scriptRuntime),
|
|
747
|
-
secrets: params.secretNames.length > 0 ? params.secretNames : void 0,
|
|
748
|
-
inputs: encodeInputs(params.inputs, params.inputTypes),
|
|
749
|
-
output_types: encodeOutputTypes(params.outputTypes)
|
|
750
|
-
};
|
|
751
|
-
try {
|
|
752
|
-
const response = await fetch(url, {
|
|
753
|
-
method: "POST",
|
|
754
|
-
headers: {
|
|
755
|
-
"Content-Type": "application/json",
|
|
756
|
-
"X-API-KEY": credentials.apiKey,
|
|
757
|
-
"X-USER-ID": credentials.userId,
|
|
758
|
-
"X-BUSINESS-ID": credentials.businessId
|
|
759
|
-
},
|
|
760
|
-
body: JSON.stringify(body)
|
|
761
|
-
});
|
|
762
|
-
const durationMs = Date.now() - start;
|
|
763
|
-
if (!response.ok) {
|
|
764
|
-
const text = await response.text();
|
|
765
|
-
return {
|
|
766
|
-
success: false,
|
|
767
|
-
outputValues: {},
|
|
768
|
-
logs: [],
|
|
769
|
-
stdout: "",
|
|
770
|
-
stderr: "",
|
|
771
|
-
error: `Remote runner returned ${String(response.status)}: ${text}`,
|
|
772
|
-
exitCode: 1,
|
|
773
|
-
durationMs
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
const data = await response.json();
|
|
777
|
-
const hasError = data.error !== null;
|
|
778
|
-
const result = {
|
|
779
|
-
success: !hasError,
|
|
780
|
-
outputValues: decodeOutputValues(data.values),
|
|
781
|
-
logs: data.logs ?? [],
|
|
782
|
-
stdout: "",
|
|
783
|
-
stderr: "",
|
|
784
|
-
exitCode: hasError ? 1 : 0,
|
|
785
|
-
durationMs: data.duration_ms ?? durationMs
|
|
786
|
-
};
|
|
787
|
-
if (hasError && data.error) {
|
|
788
|
-
result.error = data.error.detail ? `${data.error.error}
|
|
789
|
-
${data.error.detail}` : data.error.error;
|
|
1238
|
+
return base;
|
|
1239
|
+
} finally {
|
|
1240
|
+
if (tmpDir) {
|
|
1241
|
+
void rm(tmpDir, { recursive: true, force: true });
|
|
790
1242
|
}
|
|
791
|
-
return result;
|
|
792
|
-
} catch (err) {
|
|
793
|
-
return {
|
|
794
|
-
success: false,
|
|
795
|
-
outputValues: {},
|
|
796
|
-
logs: [],
|
|
797
|
-
stdout: "",
|
|
798
|
-
stderr: "",
|
|
799
|
-
error: `Remote execution failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
800
|
-
exitCode: 1,
|
|
801
|
-
durationMs: Date.now() - start
|
|
802
|
-
};
|
|
803
1243
|
}
|
|
804
1244
|
}
|
|
805
1245
|
|
|
806
1246
|
// src/server/requestHandler.ts
|
|
1247
|
+
var SOURCE_FILE_MAX_BYTES = 2 * 1024 * 1024;
|
|
1248
|
+
var SKIP_FILES = /* @__PURE__ */ new Set([".DS_Store", ".gitignore"]);
|
|
1249
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1250
|
+
".ts",
|
|
1251
|
+
".tsx",
|
|
1252
|
+
".js",
|
|
1253
|
+
".jsx",
|
|
1254
|
+
".mjs",
|
|
1255
|
+
".cjs",
|
|
1256
|
+
".json",
|
|
1257
|
+
".md",
|
|
1258
|
+
".mdx",
|
|
1259
|
+
".yml",
|
|
1260
|
+
".yaml",
|
|
1261
|
+
".toml",
|
|
1262
|
+
".css",
|
|
1263
|
+
".scss",
|
|
1264
|
+
".html",
|
|
1265
|
+
".htm",
|
|
1266
|
+
".xml",
|
|
1267
|
+
".svg",
|
|
1268
|
+
".txt",
|
|
1269
|
+
".sh",
|
|
1270
|
+
".py",
|
|
1271
|
+
".env",
|
|
1272
|
+
".gitignore",
|
|
1273
|
+
".prettierrc",
|
|
1274
|
+
".eslintrc"
|
|
1275
|
+
]);
|
|
1276
|
+
function resolveSafePath(pluginDir, relParam) {
|
|
1277
|
+
if (relParam.includes("\0")) {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1280
|
+
const rel = relParam.replace(/^\/+/, "");
|
|
1281
|
+
const abs = resolve(pluginDir, rel);
|
|
1282
|
+
if (abs !== pluginDir && !abs.startsWith(pluginDir + sep)) {
|
|
1283
|
+
return null;
|
|
1284
|
+
}
|
|
1285
|
+
return abs;
|
|
1286
|
+
}
|
|
1287
|
+
function isInsideSkippedDir(pluginDir, abs) {
|
|
1288
|
+
const rel = abs === pluginDir ? "" : abs.slice(pluginDir.length + 1);
|
|
1289
|
+
if (rel === "") {
|
|
1290
|
+
return false;
|
|
1291
|
+
}
|
|
1292
|
+
const segments = rel.split(sep);
|
|
1293
|
+
return segments.some((segment) => SKIP_DIRS.has(segment));
|
|
1294
|
+
}
|
|
807
1295
|
function proxyLogEntryToString(entry) {
|
|
808
1296
|
if (entry.kind === "info") {
|
|
809
1297
|
return entry.message;
|
|
@@ -813,17 +1301,17 @@ function proxyLogEntryToString(entry) {
|
|
|
813
1301
|
}
|
|
814
1302
|
function getViewerPath() {
|
|
815
1303
|
const filename = fileURLToPath(import.meta.url);
|
|
816
|
-
return
|
|
1304
|
+
return join9(dirname2(filename), "viewer");
|
|
817
1305
|
}
|
|
818
1306
|
async function fileExists(filePath) {
|
|
819
1307
|
try {
|
|
820
|
-
await
|
|
1308
|
+
await access3(filePath);
|
|
821
1309
|
return true;
|
|
822
1310
|
} catch {
|
|
823
1311
|
return false;
|
|
824
1312
|
}
|
|
825
1313
|
}
|
|
826
|
-
function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast, onProxyCacheChange, lastPathRef) {
|
|
1314
|
+
function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast, onProxyCacheChange, lastPathRef, pluginDir) {
|
|
827
1315
|
const proxyCache = createProxyCache({ onChange: onProxyCacheChange });
|
|
828
1316
|
return (req, res) => {
|
|
829
1317
|
void (async () => {
|
|
@@ -864,7 +1352,7 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
|
|
|
864
1352
|
return;
|
|
865
1353
|
}
|
|
866
1354
|
if (url === "/api/bundle") {
|
|
867
|
-
const bundlePath =
|
|
1355
|
+
const bundlePath = join9(process.cwd(), ".kizenapp", "bundle.json");
|
|
868
1356
|
try {
|
|
869
1357
|
const content = await readFile4(bundlePath, "utf-8");
|
|
870
1358
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
@@ -901,36 +1389,31 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
|
|
|
901
1389
|
res.end(JSON.stringify({ error: "Missing required field: script" }));
|
|
902
1390
|
return;
|
|
903
1391
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
{
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const stepParams = {
|
|
924
|
-
script: body.script,
|
|
925
|
-
scriptRuntime: body.scriptRuntime ?? "python-3.9",
|
|
926
|
-
inputs: body.inputs ?? {},
|
|
927
|
-
secrets: body.secrets ?? {}
|
|
928
|
-
};
|
|
929
|
-
if (typeof body.timeout === "number") {
|
|
930
|
-
stepParams.timeout = body.timeout;
|
|
1392
|
+
const stepParams = {
|
|
1393
|
+
script: body.script,
|
|
1394
|
+
scriptRuntime: body.scriptRuntime ?? "python-3.9",
|
|
1395
|
+
inputs: body.inputs ?? {},
|
|
1396
|
+
secrets: body.secrets ?? {},
|
|
1397
|
+
onInstallProgress: (event) => {
|
|
1398
|
+
if (event.kind === "start") {
|
|
1399
|
+
broadcast({ type: "venv-install-start" });
|
|
1400
|
+
} else if (event.kind === "log") {
|
|
1401
|
+
broadcast({
|
|
1402
|
+
type: "venv-install-log",
|
|
1403
|
+
line: event.line,
|
|
1404
|
+
stream: event.stream
|
|
1405
|
+
});
|
|
1406
|
+
} else if (event.kind === "complete") {
|
|
1407
|
+
broadcast({ type: "venv-install-complete" });
|
|
1408
|
+
} else {
|
|
1409
|
+
broadcast({ type: "venv-install-error", message: event.message });
|
|
1410
|
+
}
|
|
931
1411
|
}
|
|
932
|
-
|
|
1412
|
+
};
|
|
1413
|
+
if (typeof body.timeout === "number") {
|
|
1414
|
+
stepParams.timeout = body.timeout;
|
|
933
1415
|
}
|
|
1416
|
+
const result = await executePythonStep(stepParams);
|
|
934
1417
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
935
1418
|
res.end(JSON.stringify(result));
|
|
936
1419
|
return;
|
|
@@ -998,9 +1481,113 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
|
|
|
998
1481
|
res.end(Buffer.from(await upstream.arrayBuffer()));
|
|
999
1482
|
return;
|
|
1000
1483
|
}
|
|
1484
|
+
if (url.startsWith("/api/source/tree") && req.method === "GET") {
|
|
1485
|
+
const query = new URL(url, "http://localhost").searchParams;
|
|
1486
|
+
const relParam = query.get("path") ?? "";
|
|
1487
|
+
const abs = resolveSafePath(pluginDir, relParam);
|
|
1488
|
+
if (abs === null) {
|
|
1489
|
+
res.writeHead(403, { "Content-Type": "application/json; charset=utf-8" });
|
|
1490
|
+
res.end(JSON.stringify({ error: "Invalid path" }));
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
if (isInsideSkippedDir(pluginDir, abs)) {
|
|
1494
|
+
res.writeHead(403, { "Content-Type": "application/json; charset=utf-8" });
|
|
1495
|
+
res.end(JSON.stringify({ error: "Path is excluded" }));
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
try {
|
|
1499
|
+
const dirEntries = await readdir3(abs, { withFileTypes: true });
|
|
1500
|
+
const entries = await Promise.all(
|
|
1501
|
+
dirEntries.filter((entry) => {
|
|
1502
|
+
if (entry.isDirectory()) {
|
|
1503
|
+
return !SKIP_DIRS.has(entry.name);
|
|
1504
|
+
}
|
|
1505
|
+
if (entry.isFile()) {
|
|
1506
|
+
return !SKIP_FILES.has(entry.name);
|
|
1507
|
+
}
|
|
1508
|
+
return false;
|
|
1509
|
+
}).map(async (entry) => {
|
|
1510
|
+
const kind = entry.isDirectory() ? "dir" : "file";
|
|
1511
|
+
const result = {
|
|
1512
|
+
name: entry.name,
|
|
1513
|
+
kind
|
|
1514
|
+
};
|
|
1515
|
+
if (kind === "file") {
|
|
1516
|
+
try {
|
|
1517
|
+
const stats = await stat(join9(abs, entry.name));
|
|
1518
|
+
result.size = stats.size;
|
|
1519
|
+
} catch {
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return result;
|
|
1523
|
+
})
|
|
1524
|
+
);
|
|
1525
|
+
entries.sort((a, b) => {
|
|
1526
|
+
if (a.kind !== b.kind) {
|
|
1527
|
+
return a.kind === "dir" ? -1 : 1;
|
|
1528
|
+
}
|
|
1529
|
+
return a.name.localeCompare(b.name);
|
|
1530
|
+
});
|
|
1531
|
+
const normalizedPath = abs === pluginDir ? "" : abs.slice(pluginDir.length + 1);
|
|
1532
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1533
|
+
res.end(JSON.stringify({ path: normalizedPath, entries }));
|
|
1534
|
+
} catch (err) {
|
|
1535
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1536
|
+
res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
|
|
1537
|
+
res.end(JSON.stringify({ error: message }));
|
|
1538
|
+
}
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
if (url.startsWith("/api/source/file") && req.method === "GET") {
|
|
1542
|
+
const query = new URL(url, "http://localhost").searchParams;
|
|
1543
|
+
const relParam = query.get("path") ?? "";
|
|
1544
|
+
if (relParam === "") {
|
|
1545
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
1546
|
+
res.end(JSON.stringify({ error: "Missing path" }));
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
const abs = resolveSafePath(pluginDir, relParam);
|
|
1550
|
+
if (abs === null) {
|
|
1551
|
+
res.writeHead(403, { "Content-Type": "application/json; charset=utf-8" });
|
|
1552
|
+
res.end(JSON.stringify({ error: "Invalid path" }));
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
if (isInsideSkippedDir(pluginDir, abs)) {
|
|
1556
|
+
res.writeHead(403, { "Content-Type": "application/json; charset=utf-8" });
|
|
1557
|
+
res.end(JSON.stringify({ error: "Path is excluded" }));
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
try {
|
|
1561
|
+
const stats = await stat(abs);
|
|
1562
|
+
if (!stats.isFile()) {
|
|
1563
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
1564
|
+
res.end(JSON.stringify({ error: "Not a file" }));
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
if (stats.size > SOURCE_FILE_MAX_BYTES) {
|
|
1568
|
+
res.writeHead(413, { "Content-Type": "application/json; charset=utf-8" });
|
|
1569
|
+
res.end(JSON.stringify({ error: "File too large", size: stats.size }));
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
const ext2 = extname(abs).toLowerCase();
|
|
1573
|
+
const explicitMime = MIME_TYPES[ext2];
|
|
1574
|
+
const isKnownText = TEXT_EXTENSIONS.has(ext2);
|
|
1575
|
+
const contentType = explicitMime ?? (isKnownText ? "text/plain; charset=utf-8" : "application/octet-stream");
|
|
1576
|
+
res.writeHead(200, {
|
|
1577
|
+
"Content-Type": contentType,
|
|
1578
|
+
"Content-Length": String(stats.size)
|
|
1579
|
+
});
|
|
1580
|
+
createReadStream(abs).pipe(res);
|
|
1581
|
+
} catch (err) {
|
|
1582
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1583
|
+
res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
|
|
1584
|
+
res.end(JSON.stringify({ error: message }));
|
|
1585
|
+
}
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1001
1588
|
const rawPath = url === "/" ? "/index.html" : url;
|
|
1002
|
-
const filePath =
|
|
1003
|
-
const resolvedPath = await fileExists(filePath) ? filePath :
|
|
1589
|
+
const filePath = join9(viewerPath, rawPath);
|
|
1590
|
+
const resolvedPath = await fileExists(filePath) ? filePath : join9(viewerPath, "index.html");
|
|
1004
1591
|
const ext = extname(resolvedPath);
|
|
1005
1592
|
const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
1006
1593
|
res.writeHead(200, { "Content-Type": mimeType });
|
|
@@ -1034,7 +1621,7 @@ import { launch } from "chrome-launcher";
|
|
|
1034
1621
|
import { createRequire } from "module";
|
|
1035
1622
|
import { mkdirSync } from "fs";
|
|
1036
1623
|
import { readFile as readFile5 } from "fs/promises";
|
|
1037
|
-
import { dirname as dirname3, join as
|
|
1624
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
1038
1625
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1039
1626
|
var CDP = createRequire(import.meta.url)("chrome-remote-interface");
|
|
1040
1627
|
var CSP_HEADERS = /* @__PURE__ */ new Set([
|
|
@@ -1113,7 +1700,7 @@ async function serializeArgAsync(client, arg) {
|
|
|
1113
1700
|
}
|
|
1114
1701
|
async function installDockIcon(client) {
|
|
1115
1702
|
try {
|
|
1116
|
-
const iconPath =
|
|
1703
|
+
const iconPath = join10(dirname3(fileURLToPath2(import.meta.url)), "viewer", "icon.png");
|
|
1117
1704
|
const icon = await readFile5(iconPath);
|
|
1118
1705
|
await client.Browser.setDockTile({ image: icon.toString("base64") });
|
|
1119
1706
|
} catch {
|
|
@@ -1134,19 +1721,261 @@ async function launchChromeViewer(opts) {
|
|
|
1134
1721
|
handleSIGINT: false,
|
|
1135
1722
|
logLevel: "silent"
|
|
1136
1723
|
});
|
|
1724
|
+
const cdpPort = chrome.port;
|
|
1137
1725
|
let client = null;
|
|
1138
1726
|
let cookieTimer = null;
|
|
1139
|
-
|
|
1727
|
+
let mainTargetId = null;
|
|
1728
|
+
let reconnectTimer = null;
|
|
1729
|
+
let reconnectAttempt = 0;
|
|
1730
|
+
let killed = false;
|
|
1731
|
+
const dbg = (msg) => {
|
|
1732
|
+
opts.debugLog?.(msg);
|
|
1733
|
+
};
|
|
1734
|
+
const vb = (msg) => {
|
|
1735
|
+
if (opts.verbose) {
|
|
1736
|
+
opts.debugLog?.(msg);
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
const clearCookieTimer = () => {
|
|
1740
|
+
if (cookieTimer !== null) {
|
|
1741
|
+
clearInterval(cookieTimer);
|
|
1742
|
+
cookieTimer = null;
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
const isNewTabUrl = (url) => url === "chrome://new-tab-page/" || url === "chrome://newtab/";
|
|
1746
|
+
const setupClient = async (c) => {
|
|
1747
|
+
clearCookieTimer();
|
|
1748
|
+
mainTargetId = null;
|
|
1749
|
+
await installDockIcon(c);
|
|
1750
|
+
await c.Fetch.enable({
|
|
1751
|
+
patterns: KIZEN_DOMAINS.flatMap((d) => [
|
|
1752
|
+
{ urlPattern: `*://${d}/*`, requestStage: "Response" },
|
|
1753
|
+
{ urlPattern: `*://*.${d}/*`, requestStage: "Response" }
|
|
1754
|
+
])
|
|
1755
|
+
});
|
|
1756
|
+
c.on("Fetch.requestPaused", (rawParams) => {
|
|
1757
|
+
void (async () => {
|
|
1758
|
+
const params = rawParams;
|
|
1759
|
+
const { requestId, request, responseHeaders, responseStatusCode } = params;
|
|
1760
|
+
const url = request.url;
|
|
1761
|
+
vb(`Fetch ${String(responseStatusCode ?? "?")} ${url}`);
|
|
1762
|
+
const filtered = [];
|
|
1763
|
+
let rewroteSetCookie = false;
|
|
1764
|
+
for (const h of responseHeaders ?? []) {
|
|
1765
|
+
if (CSP_HEADERS.has(h.name.toLowerCase())) {
|
|
1766
|
+
continue;
|
|
1767
|
+
}
|
|
1768
|
+
if (h.name.toLowerCase() === "set-cookie" && isKizenUrl(url)) {
|
|
1769
|
+
const patched = h.value.split("\n").map((cookie) => {
|
|
1770
|
+
if (!/SameSite=/i.test(cookie)) {
|
|
1771
|
+
return cookie + "; SameSite=None; Secure";
|
|
1772
|
+
}
|
|
1773
|
+
return cookie.replace(/SameSite=\w+/i, "SameSite=None");
|
|
1774
|
+
}).join("\n");
|
|
1775
|
+
filtered.push({ name: h.name, value: patched });
|
|
1776
|
+
rewroteSetCookie = true;
|
|
1777
|
+
} else {
|
|
1778
|
+
filtered.push(h);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
if (rewroteSetCookie) {
|
|
1782
|
+
vb(` rewrote Set-Cookie SameSite on ${url}`);
|
|
1783
|
+
}
|
|
1784
|
+
try {
|
|
1785
|
+
const { body, base64Encoded } = await c.Fetch.getResponseBody({ requestId });
|
|
1786
|
+
await c.Fetch.fulfillRequest({
|
|
1787
|
+
requestId,
|
|
1788
|
+
responseCode: responseStatusCode ?? 200,
|
|
1789
|
+
responseHeaders: filtered,
|
|
1790
|
+
body,
|
|
1791
|
+
...base64Encoded && { base64Encoded: true }
|
|
1792
|
+
});
|
|
1793
|
+
} catch (e) {
|
|
1794
|
+
vb(` fulfillRequest failed (${e.message}), falling through`);
|
|
1795
|
+
try {
|
|
1796
|
+
await c.Fetch.continueRequest({ requestId });
|
|
1797
|
+
} catch (e2) {
|
|
1798
|
+
vb(` continueRequest also failed (${e2.message})`);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
})();
|
|
1802
|
+
});
|
|
1803
|
+
cookieTimer = setInterval(() => {
|
|
1804
|
+
void (async () => {
|
|
1805
|
+
try {
|
|
1806
|
+
const { cookies } = await c.Network.getAllCookies();
|
|
1807
|
+
let patched = 0;
|
|
1808
|
+
for (const cookie of cookies) {
|
|
1809
|
+
if (!isKizenUrl(`https://${cookie.domain ?? ""}`)) {
|
|
1810
|
+
continue;
|
|
1811
|
+
}
|
|
1812
|
+
if (cookie.sameSite === "None") {
|
|
1813
|
+
continue;
|
|
1814
|
+
}
|
|
1815
|
+
const raw = cookie.domain ?? "";
|
|
1816
|
+
const bare = raw.startsWith(".") ? raw.slice(1) : raw;
|
|
1817
|
+
await c.Network.setCookie({
|
|
1818
|
+
url: `https://${bare}`,
|
|
1819
|
+
name: cookie.name,
|
|
1820
|
+
value: cookie.value,
|
|
1821
|
+
domain: raw,
|
|
1822
|
+
path: cookie.path ?? "/",
|
|
1823
|
+
secure: true,
|
|
1824
|
+
...cookie.httpOnly !== void 0 && { httpOnly: cookie.httpOnly },
|
|
1825
|
+
...cookie.expirationDate !== void 0 && {
|
|
1826
|
+
expires: cookie.expirationDate
|
|
1827
|
+
},
|
|
1828
|
+
sameSite: "None"
|
|
1829
|
+
});
|
|
1830
|
+
patched++;
|
|
1831
|
+
}
|
|
1832
|
+
if (patched > 0) {
|
|
1833
|
+
vb(`cookie-patch cycle: updated ${String(patched)} cookie(s) to SameSite=None`);
|
|
1834
|
+
}
|
|
1835
|
+
} catch (e) {
|
|
1836
|
+
vb(`cookie-patch cycle failed: ${e.message}`);
|
|
1837
|
+
}
|
|
1838
|
+
})();
|
|
1839
|
+
}, 2e3);
|
|
1840
|
+
await c.send("Runtime.enable", { generatePreview: true });
|
|
1841
|
+
c.on("Target.attachedToTarget", (rawParams) => {
|
|
1842
|
+
const params = rawParams;
|
|
1843
|
+
const { sessionId, targetInfo } = params;
|
|
1844
|
+
vb(`attached session ${sessionId} (${targetInfo.type} ${targetInfo.url})`);
|
|
1845
|
+
void c.send("Runtime.enable", { generatePreview: true }, sessionId).catch((e) => {
|
|
1846
|
+
vb(` Runtime.enable on session ${sessionId} failed: ${e.message}`);
|
|
1847
|
+
});
|
|
1848
|
+
});
|
|
1849
|
+
await c.Target.setAutoAttach({
|
|
1850
|
+
autoAttach: true,
|
|
1851
|
+
waitForDebuggerOnStart: false,
|
|
1852
|
+
flatten: true
|
|
1853
|
+
});
|
|
1854
|
+
await c.Target.setDiscoverTargets({ discover: true });
|
|
1855
|
+
const handleTarget = (targetInfo) => {
|
|
1856
|
+
if (targetInfo.type !== "page") {
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
if (isNewTabUrl(targetInfo.url)) {
|
|
1860
|
+
dbg(`closed stray new-tab-page target ${targetInfo.targetId}`);
|
|
1861
|
+
if (mainTargetId !== null) {
|
|
1862
|
+
void c.Target.activateTarget({ targetId: mainTargetId }).catch(() => {
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
void c.Target.closeTarget({ targetId: targetInfo.targetId }).catch(() => {
|
|
1866
|
+
});
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
if (mainTargetId === null) {
|
|
1870
|
+
mainTargetId = targetInfo.targetId;
|
|
1871
|
+
dbg(`captured main target ${targetInfo.targetId} (${targetInfo.url})`);
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
c.on("Target.targetCreated", (rawParams) => {
|
|
1875
|
+
const { targetInfo } = rawParams;
|
|
1876
|
+
vb(`target +${targetInfo.type} ${targetInfo.targetId} ${targetInfo.url}`);
|
|
1877
|
+
handleTarget(targetInfo);
|
|
1878
|
+
});
|
|
1879
|
+
c.on("Target.targetInfoChanged", (rawParams) => {
|
|
1880
|
+
const { targetInfo } = rawParams;
|
|
1881
|
+
vb(`target ~${targetInfo.type} ${targetInfo.targetId} ${targetInfo.url}`);
|
|
1882
|
+
handleTarget(targetInfo);
|
|
1883
|
+
});
|
|
1884
|
+
c.on("Runtime.consoleAPICalled", (rawParams) => {
|
|
1885
|
+
void (async () => {
|
|
1886
|
+
const { type, args } = rawParams;
|
|
1887
|
+
if (args[0]?.type === "string" && String(args[0].value).includes("Chrome Security")) {
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
const level = type === "warning" ? "warn" : ["log", "warn", "error", "info"].includes(type) ? type : "log";
|
|
1891
|
+
vb(`console.${level} (${String(args.length)} arg(s))`);
|
|
1892
|
+
const serialized = await Promise.all(args.map((arg) => serializeArgAsync(c, arg)));
|
|
1893
|
+
opts.broadcast({ type: "console-message", level, args: serialized });
|
|
1894
|
+
})().catch((e) => {
|
|
1895
|
+
vb(`console serialization failed: ${e.message}`);
|
|
1896
|
+
});
|
|
1897
|
+
});
|
|
1898
|
+
c.on("disconnect", () => {
|
|
1899
|
+
if (killed || client !== c) {
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
dbg("CDP disconnected");
|
|
1903
|
+
client = null;
|
|
1904
|
+
clearCookieTimer();
|
|
1905
|
+
scheduleReconnect();
|
|
1906
|
+
});
|
|
1907
|
+
};
|
|
1908
|
+
const scheduleReconnect = () => {
|
|
1909
|
+
if (killed || reconnectTimer !== null) {
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
const delay = Math.min(1e3 * 2 ** reconnectAttempt, 1e4);
|
|
1913
|
+
const attemptNumber = reconnectAttempt + 1;
|
|
1914
|
+
reconnectAttempt++;
|
|
1915
|
+
dbg(`CDP reconnect scheduled in ${String(delay)}ms (attempt #${String(attemptNumber)})`);
|
|
1916
|
+
reconnectTimer = setTimeout(() => {
|
|
1917
|
+
reconnectTimer = null;
|
|
1918
|
+
void (async () => {
|
|
1919
|
+
let c;
|
|
1920
|
+
try {
|
|
1921
|
+
c = await CDP({ port: cdpPort });
|
|
1922
|
+
} catch {
|
|
1923
|
+
dbg(`CDP reconnect attempt #${String(attemptNumber)} failed`);
|
|
1924
|
+
scheduleReconnect();
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
if (killed) {
|
|
1928
|
+
try {
|
|
1929
|
+
await c.close();
|
|
1930
|
+
} catch {
|
|
1931
|
+
}
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
client = c;
|
|
1935
|
+
try {
|
|
1936
|
+
await setupClient(c);
|
|
1937
|
+
dbg(`CDP reconnected after ${String(attemptNumber)} attempt(s)`);
|
|
1938
|
+
reconnectAttempt = 0;
|
|
1939
|
+
} catch {
|
|
1940
|
+
if (client === c) {
|
|
1941
|
+
client = null;
|
|
1942
|
+
}
|
|
1943
|
+
try {
|
|
1944
|
+
await c.close();
|
|
1945
|
+
} catch {
|
|
1946
|
+
}
|
|
1947
|
+
dbg(`CDP reconnect attempt #${String(attemptNumber)} failed during setup`);
|
|
1948
|
+
scheduleReconnect();
|
|
1949
|
+
}
|
|
1950
|
+
})();
|
|
1951
|
+
}, delay);
|
|
1952
|
+
};
|
|
1140
1953
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
1141
1954
|
await new Promise((r) => setTimeout(r, 300));
|
|
1955
|
+
dbg(`CDP connect attempt ${String(attempt + 1)}/10`);
|
|
1142
1956
|
try {
|
|
1143
|
-
|
|
1957
|
+
const c = await CDP({ port: cdpPort });
|
|
1958
|
+
client = c;
|
|
1144
1959
|
if (opts.path) {
|
|
1145
|
-
void
|
|
1960
|
+
void c.send("Page.navigate", { url: `${baseUrl}${opts.path}` });
|
|
1961
|
+
}
|
|
1962
|
+
try {
|
|
1963
|
+
await setupClient(c);
|
|
1964
|
+
dbg("CDP connected");
|
|
1965
|
+
} catch {
|
|
1966
|
+
if (client === c) {
|
|
1967
|
+
client = null;
|
|
1968
|
+
}
|
|
1969
|
+
try {
|
|
1970
|
+
await c.close();
|
|
1971
|
+
} catch {
|
|
1972
|
+
}
|
|
1973
|
+
dbg("CDP setup failed after connect");
|
|
1146
1974
|
}
|
|
1147
1975
|
break;
|
|
1148
1976
|
} catch {
|
|
1149
1977
|
if (attempt === 9) {
|
|
1978
|
+
dbg("CDP unavailable after 10 attempts \u2014 viewer will run without interception");
|
|
1150
1979
|
return {
|
|
1151
1980
|
kill: () => {
|
|
1152
1981
|
chrome.kill();
|
|
@@ -1162,120 +1991,20 @@ async function launchChromeViewer(opts) {
|
|
|
1162
1991
|
}
|
|
1163
1992
|
};
|
|
1164
1993
|
}
|
|
1165
|
-
const
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
void (async () => {
|
|
1175
|
-
const params = rawParams;
|
|
1176
|
-
const { requestId, request, responseHeaders, responseStatusCode } = params;
|
|
1177
|
-
const url = request.url;
|
|
1178
|
-
const filtered = [];
|
|
1179
|
-
for (const h of responseHeaders ?? []) {
|
|
1180
|
-
if (CSP_HEADERS.has(h.name.toLowerCase())) {
|
|
1181
|
-
continue;
|
|
1182
|
-
}
|
|
1183
|
-
if (h.name.toLowerCase() === "set-cookie" && isKizenUrl(url)) {
|
|
1184
|
-
const patched = h.value.split("\n").map((cookie) => {
|
|
1185
|
-
if (!/SameSite=/i.test(cookie)) {
|
|
1186
|
-
return cookie + "; SameSite=None; Secure";
|
|
1187
|
-
}
|
|
1188
|
-
return cookie.replace(/SameSite=\w+/i, "SameSite=None");
|
|
1189
|
-
}).join("\n");
|
|
1190
|
-
filtered.push({ name: h.name, value: patched });
|
|
1191
|
-
} else {
|
|
1192
|
-
filtered.push(h);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
try {
|
|
1196
|
-
const { body, base64Encoded } = await c.Fetch.getResponseBody({ requestId });
|
|
1197
|
-
await c.Fetch.fulfillRequest({
|
|
1198
|
-
requestId,
|
|
1199
|
-
responseCode: responseStatusCode ?? 200,
|
|
1200
|
-
responseHeaders: filtered,
|
|
1201
|
-
body,
|
|
1202
|
-
...base64Encoded && { base64Encoded: true }
|
|
1203
|
-
});
|
|
1204
|
-
} catch {
|
|
1205
|
-
try {
|
|
1206
|
-
await c.Fetch.continueRequest({ requestId });
|
|
1207
|
-
} catch {
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
})();
|
|
1211
|
-
});
|
|
1212
|
-
cookieTimer = setInterval(() => {
|
|
1213
|
-
void (async () => {
|
|
1994
|
+
const kill = () => {
|
|
1995
|
+
dbg("CDP kill requested \u2014 tearing down");
|
|
1996
|
+
killed = true;
|
|
1997
|
+
if (reconnectTimer !== null) {
|
|
1998
|
+
clearTimeout(reconnectTimer);
|
|
1999
|
+
reconnectTimer = null;
|
|
2000
|
+
}
|
|
2001
|
+
clearCookieTimer();
|
|
2002
|
+
if (client !== null) {
|
|
1214
2003
|
try {
|
|
1215
|
-
|
|
1216
|
-
for (const cookie of cookies) {
|
|
1217
|
-
if (!isKizenUrl(`https://${cookie.domain ?? ""}`)) {
|
|
1218
|
-
continue;
|
|
1219
|
-
}
|
|
1220
|
-
if (cookie.sameSite === "None") {
|
|
1221
|
-
continue;
|
|
1222
|
-
}
|
|
1223
|
-
const raw = cookie.domain ?? "";
|
|
1224
|
-
const bare = raw.startsWith(".") ? raw.slice(1) : raw;
|
|
1225
|
-
await c.Network.setCookie({
|
|
1226
|
-
url: `https://${bare}`,
|
|
1227
|
-
name: cookie.name,
|
|
1228
|
-
value: cookie.value,
|
|
1229
|
-
domain: raw,
|
|
1230
|
-
path: cookie.path ?? "/",
|
|
1231
|
-
secure: true,
|
|
1232
|
-
...cookie.httpOnly !== void 0 && { httpOnly: cookie.httpOnly },
|
|
1233
|
-
...cookie.expirationDate !== void 0 && {
|
|
1234
|
-
expires: cookie.expirationDate
|
|
1235
|
-
},
|
|
1236
|
-
sameSite: "None"
|
|
1237
|
-
});
|
|
1238
|
-
}
|
|
2004
|
+
void client.close();
|
|
1239
2005
|
} catch {
|
|
1240
2006
|
}
|
|
1241
|
-
|
|
1242
|
-
}, 2e3);
|
|
1243
|
-
await c.send("Runtime.enable", { generatePreview: true });
|
|
1244
|
-
c.on("Target.attachedToTarget", (rawParams) => {
|
|
1245
|
-
const { sessionId } = rawParams;
|
|
1246
|
-
void c.send("Runtime.enable", { generatePreview: true }, sessionId).catch(() => {
|
|
1247
|
-
});
|
|
1248
|
-
});
|
|
1249
|
-
await c.Target.setAutoAttach({
|
|
1250
|
-
autoAttach: true,
|
|
1251
|
-
waitForDebuggerOnStart: false,
|
|
1252
|
-
flatten: true
|
|
1253
|
-
});
|
|
1254
|
-
await c.Target.setDiscoverTargets({ discover: true });
|
|
1255
|
-
c.on("Target.targetCreated", (rawParams) => {
|
|
1256
|
-
const { targetInfo } = rawParams;
|
|
1257
|
-
if (targetInfo.type === "page" && targetInfo.url === "chrome://newtab/") {
|
|
1258
|
-
void c.Target.closeTarget({ targetId: targetInfo.targetId }).catch(() => {
|
|
1259
|
-
});
|
|
1260
|
-
}
|
|
1261
|
-
});
|
|
1262
|
-
c.on("Runtime.consoleAPICalled", (rawParams) => {
|
|
1263
|
-
void (async () => {
|
|
1264
|
-
const { type, args } = rawParams;
|
|
1265
|
-
if (args[0]?.type === "string" && String(args[0].value).includes("Chrome Security")) {
|
|
1266
|
-
return;
|
|
1267
|
-
}
|
|
1268
|
-
const level = type === "warning" ? "warn" : ["log", "warn", "error", "info"].includes(type) ? type : "log";
|
|
1269
|
-
const serialized = await Promise.all(args.map((arg) => serializeArgAsync(c, arg)));
|
|
1270
|
-
opts.broadcast({ type: "console-message", level, args: serialized });
|
|
1271
|
-
})().catch(() => {
|
|
1272
|
-
});
|
|
1273
|
-
});
|
|
1274
|
-
const kill = () => {
|
|
1275
|
-
clearInterval(cookieTimer);
|
|
1276
|
-
try {
|
|
1277
|
-
void c.close();
|
|
1278
|
-
} catch {
|
|
2007
|
+
client = null;
|
|
1279
2008
|
}
|
|
1280
2009
|
chrome.kill();
|
|
1281
2010
|
};
|
|
@@ -1283,10 +2012,10 @@ async function launchChromeViewer(opts) {
|
|
|
1283
2012
|
}
|
|
1284
2013
|
|
|
1285
2014
|
// src/hooks/useLogHistory.ts
|
|
1286
|
-
import { useCallback, useState as
|
|
2015
|
+
import { useCallback as useCallback2, useState as useState4 } from "react";
|
|
1287
2016
|
function useLogHistory(limit) {
|
|
1288
|
-
const [history, setHistory] =
|
|
1289
|
-
const append =
|
|
2017
|
+
const [history, setHistory] = useState4([]);
|
|
2018
|
+
const append = useCallback2(
|
|
1290
2019
|
(message) => {
|
|
1291
2020
|
setHistory((h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-limit));
|
|
1292
2021
|
},
|
|
@@ -1296,29 +2025,29 @@ function useLogHistory(limit) {
|
|
|
1296
2025
|
}
|
|
1297
2026
|
|
|
1298
2027
|
// src/ui/CredentialSetupUI.tsx
|
|
1299
|
-
import { useCallback as
|
|
1300
|
-
import { Box as
|
|
1301
|
-
import { jsx as
|
|
1302
|
-
var
|
|
1303
|
-
var
|
|
2028
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useState as useState5 } from "react";
|
|
2029
|
+
import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
|
|
2030
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2031
|
+
var FIELDS2 = ["apiKey", "userId", "businessId"];
|
|
2032
|
+
var FIELD_LABELS2 = {
|
|
1304
2033
|
apiKey: "API Key",
|
|
1305
2034
|
userId: "User ID",
|
|
1306
2035
|
businessId: "Business ID"
|
|
1307
2036
|
};
|
|
1308
|
-
var
|
|
2037
|
+
var Hint2 = ({ text }) => /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: text });
|
|
1309
2038
|
var CredentialSetupUI = ({
|
|
1310
2039
|
initialMode,
|
|
1311
2040
|
showProfileManager,
|
|
1312
2041
|
onComplete,
|
|
1313
2042
|
onCancel
|
|
1314
2043
|
}) => {
|
|
1315
|
-
const [phase, setPhase] =
|
|
2044
|
+
const [phase, setPhase] = useState5(
|
|
1316
2045
|
initialMode === "global" && !showProfileManager ? { type: "loading" } : initialMode === "global" && showProfileManager ? { type: "profile-loading" } : { type: "mode-select", cursor: 0 }
|
|
1317
2046
|
);
|
|
1318
|
-
const [inputBuffer, setInputBuffer] =
|
|
1319
|
-
const [error, setError] =
|
|
1320
|
-
const [activeProfileName, setActiveProfileName] =
|
|
1321
|
-
const handleModeChosen =
|
|
2047
|
+
const [inputBuffer, setInputBuffer] = useState5("");
|
|
2048
|
+
const [error, setError] = useState5(null);
|
|
2049
|
+
const [activeProfileName, setActiveProfileName] = useState5(void 0);
|
|
2050
|
+
const handleModeChosen = useCallback3(
|
|
1322
2051
|
(mode) => {
|
|
1323
2052
|
if (mode === "local") {
|
|
1324
2053
|
onComplete({ mode: "local", credentials: null });
|
|
@@ -1328,7 +2057,7 @@ var CredentialSetupUI = ({
|
|
|
1328
2057
|
},
|
|
1329
2058
|
[onComplete]
|
|
1330
2059
|
);
|
|
1331
|
-
const handleProfileChosen =
|
|
2060
|
+
const handleProfileChosen = useCallback3(async (profile) => {
|
|
1332
2061
|
if (profile === null) {
|
|
1333
2062
|
setPhase({ type: "profile-loading" });
|
|
1334
2063
|
const profiles = await listCredentialProfiles();
|
|
@@ -1341,7 +2070,7 @@ var CredentialSetupUI = ({
|
|
|
1341
2070
|
const envCursor2 = existing ? Math.max(0, ENVIRONMENTS.indexOf(existing.environment)) : 0;
|
|
1342
2071
|
setPhase({ type: "creds-entry", field: 0, values: existing ?? {}, envCursor: envCursor2 });
|
|
1343
2072
|
}, []);
|
|
1344
|
-
const handleSave =
|
|
2073
|
+
const handleSave = useCallback3(
|
|
1345
2074
|
async (values2, envCursor2) => {
|
|
1346
2075
|
const environment = ENVIRONMENTS[envCursor2] ?? "go";
|
|
1347
2076
|
const credentials = {
|
|
@@ -1366,7 +2095,7 @@ var CredentialSetupUI = ({
|
|
|
1366
2095
|
},
|
|
1367
2096
|
[onComplete, activeProfileName]
|
|
1368
2097
|
);
|
|
1369
|
-
|
|
2098
|
+
useInput2((input, key) => {
|
|
1370
2099
|
if (key.ctrl && input === "c") {
|
|
1371
2100
|
process.exit(0);
|
|
1372
2101
|
}
|
|
@@ -1440,7 +2169,7 @@ var CredentialSetupUI = ({
|
|
|
1440
2169
|
}
|
|
1441
2170
|
if (phase.type === "creds-entry") {
|
|
1442
2171
|
const { field, values: values2, envCursor: envCursor2 } = phase;
|
|
1443
|
-
const isEnvField2 = field ===
|
|
2172
|
+
const isEnvField2 = field === FIELDS2.length;
|
|
1444
2173
|
if (isEnvField2) {
|
|
1445
2174
|
if (key.leftArrow) {
|
|
1446
2175
|
setPhase({
|
|
@@ -1451,13 +2180,13 @@ var CredentialSetupUI = ({
|
|
|
1451
2180
|
setPhase({ ...phase, envCursor: (envCursor2 + 1) % ENVIRONMENTS.length });
|
|
1452
2181
|
} else if (key.upArrow) {
|
|
1453
2182
|
setPhase({ ...phase, field: field - 1 });
|
|
1454
|
-
setInputBuffer(values2[
|
|
2183
|
+
setInputBuffer(values2[FIELDS2[field - 1]] ?? "");
|
|
1455
2184
|
} else if (key.return) {
|
|
1456
2185
|
void handleSave(values2, envCursor2);
|
|
1457
2186
|
}
|
|
1458
2187
|
return;
|
|
1459
2188
|
}
|
|
1460
|
-
const fieldName =
|
|
2189
|
+
const fieldName = FIELDS2[field];
|
|
1461
2190
|
if (!fieldName) {
|
|
1462
2191
|
return;
|
|
1463
2192
|
}
|
|
@@ -1467,13 +2196,13 @@ var CredentialSetupUI = ({
|
|
|
1467
2196
|
const updatedValues = { ...values2, [fieldName]: inputBuffer };
|
|
1468
2197
|
const prevField = field - 1;
|
|
1469
2198
|
setPhase({ ...phase, field: prevField, values: updatedValues });
|
|
1470
|
-
setInputBuffer(updatedValues[
|
|
2199
|
+
setInputBuffer(updatedValues[FIELDS2[prevField]] ?? "");
|
|
1471
2200
|
} else if (key.return || key.tab || key.downArrow) {
|
|
1472
2201
|
const updatedValues = { ...values2, [fieldName]: inputBuffer };
|
|
1473
2202
|
const nextField = field + 1;
|
|
1474
2203
|
setPhase({ ...phase, field: nextField, values: updatedValues });
|
|
1475
|
-
if (nextField <
|
|
1476
|
-
setInputBuffer(updatedValues[
|
|
2204
|
+
if (nextField < FIELDS2.length) {
|
|
2205
|
+
setInputBuffer(updatedValues[FIELDS2[nextField]] ?? "");
|
|
1477
2206
|
} else {
|
|
1478
2207
|
setInputBuffer("");
|
|
1479
2208
|
}
|
|
@@ -1482,7 +2211,7 @@ var CredentialSetupUI = ({
|
|
|
1482
2211
|
}
|
|
1483
2212
|
}
|
|
1484
2213
|
});
|
|
1485
|
-
const loadProfileList =
|
|
2214
|
+
const loadProfileList = useCallback3(async () => {
|
|
1486
2215
|
const profiles = await listCredentialProfiles();
|
|
1487
2216
|
setPhase({ type: "profile-select", profiles, cursor: 0 });
|
|
1488
2217
|
}, []);
|
|
@@ -1507,7 +2236,7 @@ var CredentialSetupUI = ({
|
|
|
1507
2236
|
}, [phase.type]);
|
|
1508
2237
|
if (phase.type === "loading" || phase.type === "saving" || phase.type === "profile-loading") {
|
|
1509
2238
|
const label = phase.type === "saving" ? "Saving credentials\u2026" : phase.type === "profile-loading" ? "Loading profiles\u2026" : "Loading credentials\u2026";
|
|
1510
|
-
return /* @__PURE__ */
|
|
2239
|
+
return /* @__PURE__ */ jsx6(Box5, { paddingY: 1, paddingX: 2, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: label }) });
|
|
1511
2240
|
}
|
|
1512
2241
|
if (phase.type === "done") {
|
|
1513
2242
|
return null;
|
|
@@ -1517,41 +2246,41 @@ var CredentialSetupUI = ({
|
|
|
1517
2246
|
{ label: "Global", desc: `~/.kizenappbuilder/credentials.json`, mode: "global" },
|
|
1518
2247
|
{ label: "Local", desc: "Enter credentials in the browser dev tools", mode: "local" }
|
|
1519
2248
|
];
|
|
1520
|
-
return /* @__PURE__ */
|
|
1521
|
-
/* @__PURE__ */
|
|
1522
|
-
/* @__PURE__ */
|
|
1523
|
-
/* @__PURE__ */
|
|
2249
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
2250
|
+
/* @__PURE__ */ jsx6(AppHeader, {}),
|
|
2251
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 0, children: [
|
|
2252
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: "Credential mode" }),
|
|
1524
2253
|
options.map((opt, i) => {
|
|
1525
2254
|
const selected = phase.cursor === i;
|
|
1526
|
-
return /* @__PURE__ */
|
|
1527
|
-
/* @__PURE__ */
|
|
1528
|
-
/* @__PURE__ */
|
|
1529
|
-
/* @__PURE__ */
|
|
2255
|
+
return /* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
2256
|
+
/* @__PURE__ */ jsx6(Text6, { ...selected && { color: "cyan" }, children: selected ? "\u276F" : " " }),
|
|
2257
|
+
/* @__PURE__ */ jsx6(Text6, { bold: selected, ...selected && { color: "cyan" }, children: opt.label }),
|
|
2258
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: opt.desc })
|
|
1530
2259
|
] }, opt.mode);
|
|
1531
2260
|
})
|
|
1532
2261
|
] }),
|
|
1533
|
-
/* @__PURE__ */
|
|
2262
|
+
/* @__PURE__ */ jsx6(Hint2, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit" })
|
|
1534
2263
|
] });
|
|
1535
2264
|
}
|
|
1536
2265
|
if (phase.type === "profile-select") {
|
|
1537
2266
|
const { profiles, cursor } = phase;
|
|
1538
2267
|
const addNewIdx = profiles.length;
|
|
1539
|
-
return /* @__PURE__ */
|
|
1540
|
-
/* @__PURE__ */
|
|
1541
|
-
/* @__PURE__ */
|
|
1542
|
-
/* @__PURE__ */
|
|
2268
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
2269
|
+
/* @__PURE__ */ jsx6(AppHeader, {}),
|
|
2270
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 0, children: [
|
|
2271
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: "Credential profiles" }),
|
|
1543
2272
|
profiles.map((profile, i) => {
|
|
1544
2273
|
const selected = cursor === i;
|
|
1545
|
-
return /* @__PURE__ */
|
|
1546
|
-
/* @__PURE__ */
|
|
1547
|
-
/* @__PURE__ */
|
|
1548
|
-
/* @__PURE__ */
|
|
2274
|
+
return /* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
2275
|
+
/* @__PURE__ */ jsx6(Text6, { ...selected && { color: "cyan" }, children: selected ? "\u276F" : " " }),
|
|
2276
|
+
/* @__PURE__ */ jsx6(Text6, { bold: selected, ...selected && { color: "cyan" }, children: profile.isDefault ? "Default" : profile.name }),
|
|
2277
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: profile.path })
|
|
1549
2278
|
] }, profile.name);
|
|
1550
2279
|
}),
|
|
1551
|
-
/* @__PURE__ */
|
|
1552
|
-
/* @__PURE__ */
|
|
1553
|
-
/* @__PURE__ */
|
|
1554
|
-
|
|
2280
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
2281
|
+
/* @__PURE__ */ jsx6(Text6, { ...cursor === addNewIdx && { color: "cyan" }, children: cursor === addNewIdx ? "\u276F" : " " }),
|
|
2282
|
+
/* @__PURE__ */ jsx6(
|
|
2283
|
+
Text6,
|
|
1555
2284
|
{
|
|
1556
2285
|
bold: cursor === addNewIdx,
|
|
1557
2286
|
...cursor === addNewIdx && { color: "cyan" },
|
|
@@ -1560,69 +2289,69 @@ var CredentialSetupUI = ({
|
|
|
1560
2289
|
)
|
|
1561
2290
|
] })
|
|
1562
2291
|
] }),
|
|
1563
|
-
/* @__PURE__ */
|
|
2292
|
+
/* @__PURE__ */ jsx6(Hint2, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel" })
|
|
1564
2293
|
] });
|
|
1565
2294
|
}
|
|
1566
2295
|
if (phase.type === "name-entry") {
|
|
1567
2296
|
const { nameBuffer, nameError } = phase;
|
|
1568
|
-
return /* @__PURE__ */
|
|
1569
|
-
/* @__PURE__ */
|
|
1570
|
-
/* @__PURE__ */
|
|
1571
|
-
nameError && /* @__PURE__ */
|
|
2297
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
2298
|
+
/* @__PURE__ */ jsx6(AppHeader, {}),
|
|
2299
|
+
/* @__PURE__ */ jsx6(Box5, { flexDirection: "column", gap: 0, children: /* @__PURE__ */ jsx6(Text6, { bold: true, children: "New credential profile" }) }),
|
|
2300
|
+
nameError && /* @__PURE__ */ jsxs4(Text6, { color: "red", children: [
|
|
1572
2301
|
"Error: ",
|
|
1573
2302
|
nameError
|
|
1574
2303
|
] }),
|
|
1575
|
-
/* @__PURE__ */
|
|
1576
|
-
/* @__PURE__ */
|
|
1577
|
-
/* @__PURE__ */
|
|
1578
|
-
/* @__PURE__ */
|
|
1579
|
-
/* @__PURE__ */
|
|
2304
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
2305
|
+
/* @__PURE__ */ jsx6(Box5, { width: 12, children: /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: "Profile name" }) }),
|
|
2306
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: ">" }),
|
|
2307
|
+
/* @__PURE__ */ jsx6(Text6, { children: nameBuffer }),
|
|
2308
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u2588" })
|
|
1580
2309
|
] }),
|
|
1581
|
-
/* @__PURE__ */
|
|
2310
|
+
/* @__PURE__ */ jsx6(Hint2, { text: "Type a name \xB7 Enter to confirm \xB7 Esc to go back" })
|
|
1582
2311
|
] });
|
|
1583
2312
|
}
|
|
1584
2313
|
const { field: activeField, values, envCursor } = phase;
|
|
1585
|
-
const isEnvField = activeField ===
|
|
2314
|
+
const isEnvField = activeField === FIELDS2.length;
|
|
1586
2315
|
const profileLabel = activeProfileName ?? DEFAULT_PROFILE_NAME;
|
|
1587
|
-
return /* @__PURE__ */
|
|
1588
|
-
/* @__PURE__ */
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__PURE__ */
|
|
1591
|
-
/* @__PURE__ */
|
|
2316
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
|
|
2317
|
+
/* @__PURE__ */ jsx6(AppHeader, {}),
|
|
2318
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 0, children: [
|
|
2319
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: activeProfileName !== void 0 ? `Profile: ${activeProfileName}` : "Global credentials" }),
|
|
2320
|
+
/* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
1592
2321
|
"Saved to:",
|
|
1593
2322
|
" ",
|
|
1594
2323
|
activeProfileName !== void 0 ? `~/.kizenappbuilder/${activeProfileName}.json` : GLOBAL_CREDENTIALS_PATH
|
|
1595
2324
|
] })
|
|
1596
2325
|
] }),
|
|
1597
|
-
error && /* @__PURE__ */
|
|
2326
|
+
error && /* @__PURE__ */ jsxs4(Text6, { color: "red", children: [
|
|
1598
2327
|
"Error: ",
|
|
1599
2328
|
error
|
|
1600
2329
|
] }),
|
|
1601
|
-
/* @__PURE__ */
|
|
1602
|
-
|
|
2330
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 0, children: [
|
|
2331
|
+
FIELDS2.map((name, i) => {
|
|
1603
2332
|
const isActive = activeField === i;
|
|
1604
2333
|
const displayValue = isActive ? inputBuffer : values[name] ?? "";
|
|
1605
|
-
return /* @__PURE__ */
|
|
1606
|
-
/* @__PURE__ */
|
|
1607
|
-
/* @__PURE__ */
|
|
1608
|
-
/* @__PURE__ */
|
|
1609
|
-
isActive && /* @__PURE__ */
|
|
2334
|
+
return /* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
2335
|
+
/* @__PURE__ */ jsx6(Box5, { width: 12, children: /* @__PURE__ */ jsx6(Text6, { bold: isActive, ...isActive && { color: "cyan" }, children: FIELD_LABELS2[name] }) }),
|
|
2336
|
+
/* @__PURE__ */ jsx6(Text6, { ...isActive && { color: "cyan" }, children: ">" }),
|
|
2337
|
+
/* @__PURE__ */ jsx6(Text6, { children: displayValue }),
|
|
2338
|
+
isActive && /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u2588" })
|
|
1610
2339
|
] }, name);
|
|
1611
2340
|
}),
|
|
1612
|
-
/* @__PURE__ */
|
|
1613
|
-
/* @__PURE__ */
|
|
1614
|
-
/* @__PURE__ */
|
|
1615
|
-
/* @__PURE__ */
|
|
2341
|
+
/* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
2342
|
+
/* @__PURE__ */ jsx6(Box5, { width: 12, children: /* @__PURE__ */ jsx6(Text6, { bold: isEnvField, ...isEnvField && { color: "cyan" }, children: "Environment" }) }),
|
|
2343
|
+
/* @__PURE__ */ jsx6(Text6, { ...isEnvField && { color: "cyan" }, children: ">" }),
|
|
2344
|
+
/* @__PURE__ */ jsx6(Box5, { gap: 1, children: ENVIRONMENTS.map((env, i) => {
|
|
1616
2345
|
const isSelected = i === envCursor;
|
|
1617
2346
|
if (isEnvField) {
|
|
1618
|
-
return /* @__PURE__ */
|
|
2347
|
+
return /* @__PURE__ */ jsx6(Text6, { bold: isSelected, ...isSelected && { color: "cyan" }, children: isSelected ? `[${env}]` : env }, env);
|
|
1619
2348
|
}
|
|
1620
|
-
return /* @__PURE__ */
|
|
2349
|
+
return /* @__PURE__ */ jsx6(Text6, { dimColor: !isSelected, bold: isSelected, children: env }, env);
|
|
1621
2350
|
}) })
|
|
1622
2351
|
] })
|
|
1623
2352
|
] }),
|
|
1624
|
-
isEnvField ? /* @__PURE__ */
|
|
1625
|
-
/* @__PURE__ */
|
|
2353
|
+
isEnvField ? /* @__PURE__ */ jsx6(Hint2, { text: "\u2190\u2192 to select \xB7 \u2191\u2193 to move \xB7 Enter to save \xB7 Esc to cancel" }) : /* @__PURE__ */ jsx6(Hint2, { text: "\u2191\u2193 to move \xB7 Backspace to delete \xB7 Esc to cancel" }),
|
|
2354
|
+
/* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
1626
2355
|
"Profile: ",
|
|
1627
2356
|
profileLabel
|
|
1628
2357
|
] })
|
|
@@ -1630,7 +2359,7 @@ var CredentialSetupUI = ({
|
|
|
1630
2359
|
};
|
|
1631
2360
|
|
|
1632
2361
|
// src/ui/DevUI.tsx
|
|
1633
|
-
import { jsx as
|
|
2362
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1634
2363
|
var SKIP_WATCH_PREFIXES = [".kizenapp", ".git"];
|
|
1635
2364
|
var DevUI = ({
|
|
1636
2365
|
port,
|
|
@@ -1639,35 +2368,38 @@ var DevUI = ({
|
|
|
1639
2368
|
credentials: initialCredentials,
|
|
1640
2369
|
credentialMode: initialCredentialMode,
|
|
1641
2370
|
activeCredentialProfile: initialActiveProfile,
|
|
1642
|
-
lastPath: initialLastPath
|
|
2371
|
+
lastPath: initialLastPath,
|
|
2372
|
+
debug = false,
|
|
2373
|
+
verbose = false
|
|
1643
2374
|
}) => {
|
|
1644
2375
|
const credentialsRef = useRef(initialCredentials);
|
|
1645
2376
|
const activeProfileRef = useRef(initialActiveProfile);
|
|
1646
2377
|
const lastPathRef = useRef(initialLastPath);
|
|
1647
|
-
const [credMode, setCredMode] =
|
|
1648
|
-
const [credentialMode, setCredentialMode] =
|
|
2378
|
+
const [credMode, setCredMode] = useState6("main");
|
|
2379
|
+
const [credentialMode, setCredentialMode] = useState6(
|
|
1649
2380
|
initialCredentialMode
|
|
1650
2381
|
);
|
|
1651
|
-
const [status, setStatus] =
|
|
1652
|
-
const [errorMessage, setErrorMessage] =
|
|
2382
|
+
const [status, setStatus] = useState6("starting");
|
|
2383
|
+
const [errorMessage, setErrorMessage] = useState6(null);
|
|
1653
2384
|
const [serverLogHistory, appendServerLog] = useLogHistory(LOG_LIMIT);
|
|
1654
2385
|
const [buildLogHistory, appendBuildLog] = useLogHistory(LOG_LIMIT);
|
|
1655
|
-
const [
|
|
1656
|
-
const [
|
|
1657
|
-
const [
|
|
1658
|
-
const [
|
|
1659
|
-
const [
|
|
1660
|
-
const [
|
|
1661
|
-
const [
|
|
1662
|
-
const [
|
|
1663
|
-
const [
|
|
2386
|
+
const [cdpLogHistory, appendCdpLog] = useLogHistory(LOG_LIMIT);
|
|
2387
|
+
const [proxyLogHistory, setProxyLogHistory] = useState6([]);
|
|
2388
|
+
const [buildStatus, setBuildStatus] = useState6("pending");
|
|
2389
|
+
const [buildError, setBuildError] = useState6(null);
|
|
2390
|
+
const [lastBuilt, setLastBuilt] = useState6(null);
|
|
2391
|
+
const [lastBundleSize, setLastBundleSize] = useState6(null);
|
|
2392
|
+
const [wsClientCount, setWsClientCount] = useState6(0);
|
|
2393
|
+
const [proxyCacheSize, setProxyCacheSize] = useState6(0);
|
|
2394
|
+
const [proxyCacheHits, setProxyCacheHits] = useState6(0);
|
|
2395
|
+
const [proxyCacheMisses, setProxyCacheMisses] = useState6(0);
|
|
1664
2396
|
const buildingRef = useRef(false);
|
|
1665
2397
|
const viewerLaunchedRef = useRef(false);
|
|
1666
2398
|
const chromeHandleRef = useRef(null);
|
|
1667
2399
|
const debounceTimerRef = useRef(null);
|
|
1668
2400
|
const wsClientsRef = useRef(/* @__PURE__ */ new Set());
|
|
1669
2401
|
const pendingMessagesRef = useRef([]);
|
|
1670
|
-
const broadcast =
|
|
2402
|
+
const broadcast = useCallback4((msg) => {
|
|
1671
2403
|
const json = JSON.stringify(msg);
|
|
1672
2404
|
if (wsClientsRef.current.size === 0) {
|
|
1673
2405
|
pendingMessagesRef.current = [...pendingMessagesRef.current, json].slice(-LOG_LIMIT);
|
|
@@ -1680,22 +2412,30 @@ var DevUI = ({
|
|
|
1680
2412
|
}
|
|
1681
2413
|
}, []);
|
|
1682
2414
|
const createServerLog = appendServerLog;
|
|
1683
|
-
const launchChromeViewerCb =
|
|
2415
|
+
const launchChromeViewerCb = useCallback4(() => {
|
|
1684
2416
|
chromeHandleRef.current?.kill();
|
|
1685
2417
|
chromeHandleRef.current = null;
|
|
1686
2418
|
void launchChromeViewer({
|
|
1687
2419
|
port,
|
|
1688
|
-
userDataDir:
|
|
2420
|
+
userDataDir: join11(outputDir, ".chrome"),
|
|
1689
2421
|
broadcast,
|
|
1690
|
-
...lastPathRef.current !== void 0 && { path: lastPathRef.current }
|
|
2422
|
+
...lastPathRef.current !== void 0 && { path: lastPathRef.current },
|
|
2423
|
+
...debug && { debugLog: appendCdpLog },
|
|
2424
|
+
...verbose && { verbose: true }
|
|
1691
2425
|
}).then((handle) => {
|
|
1692
2426
|
chromeHandleRef.current = handle;
|
|
1693
|
-
process.on("exit", () => {
|
|
1694
|
-
handle.kill();
|
|
1695
|
-
});
|
|
1696
2427
|
});
|
|
1697
|
-
}, [port, outputDir, broadcast]);
|
|
1698
|
-
|
|
2428
|
+
}, [port, outputDir, broadcast, debug, verbose, appendCdpLog]);
|
|
2429
|
+
useEffect4(() => {
|
|
2430
|
+
const onExit = () => {
|
|
2431
|
+
chromeHandleRef.current?.kill();
|
|
2432
|
+
};
|
|
2433
|
+
process.on("exit", onExit);
|
|
2434
|
+
return () => {
|
|
2435
|
+
process.off("exit", onExit);
|
|
2436
|
+
};
|
|
2437
|
+
}, []);
|
|
2438
|
+
useInput3(
|
|
1699
2439
|
(input, key) => {
|
|
1700
2440
|
if (input === "q" || key.ctrl && input === "c") {
|
|
1701
2441
|
process.exit(0);
|
|
@@ -1716,7 +2456,7 @@ var DevUI = ({
|
|
|
1716
2456
|
viewerLaunchedRef.current = true;
|
|
1717
2457
|
launchChromeViewerCb();
|
|
1718
2458
|
}, [status, launchChromeViewerCb]);
|
|
1719
|
-
const handleCredentialsDone =
|
|
2459
|
+
const handleCredentialsDone = useCallback4(
|
|
1720
2460
|
(result) => {
|
|
1721
2461
|
credentialsRef.current = result.credentials;
|
|
1722
2462
|
activeProfileRef.current = result.profileName;
|
|
@@ -1731,7 +2471,7 @@ var DevUI = ({
|
|
|
1731
2471
|
},
|
|
1732
2472
|
[outputDir, broadcast]
|
|
1733
2473
|
);
|
|
1734
|
-
const createProxyLog =
|
|
2474
|
+
const createProxyLog = useCallback4(
|
|
1735
2475
|
(entry) => {
|
|
1736
2476
|
setProxyLogHistory(
|
|
1737
2477
|
(h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${proxyLogEntryToString(entry)}`].slice(
|
|
@@ -1749,14 +2489,14 @@ var DevUI = ({
|
|
|
1749
2489
|
},
|
|
1750
2490
|
[broadcast]
|
|
1751
2491
|
);
|
|
1752
|
-
const createBuildLog =
|
|
2492
|
+
const createBuildLog = useCallback4(
|
|
1753
2493
|
(message) => {
|
|
1754
2494
|
appendBuildLog(message);
|
|
1755
2495
|
broadcast({ type: "log", message });
|
|
1756
2496
|
},
|
|
1757
2497
|
[appendBuildLog, broadcast]
|
|
1758
2498
|
);
|
|
1759
|
-
const triggerBuild =
|
|
2499
|
+
const triggerBuild = useCallback4(() => {
|
|
1760
2500
|
if (buildingRef.current) {
|
|
1761
2501
|
return;
|
|
1762
2502
|
}
|
|
@@ -1808,7 +2548,7 @@ var DevUI = ({
|
|
|
1808
2548
|
}, [pluginDir, triggerBuild, createBuildLog]);
|
|
1809
2549
|
useEffect4(() => {
|
|
1810
2550
|
const viewerPath = getViewerPath();
|
|
1811
|
-
void fileExists(
|
|
2551
|
+
void fileExists(join11(viewerPath, "index.html")).then((viewerBuilt) => {
|
|
1812
2552
|
if (!viewerBuilt) {
|
|
1813
2553
|
setErrorMessage("Viewer not built. Run 'pnpm build:viewer' first.");
|
|
1814
2554
|
setStatus("error");
|
|
@@ -1823,7 +2563,8 @@ var DevUI = ({
|
|
|
1823
2563
|
outputDir,
|
|
1824
2564
|
broadcast,
|
|
1825
2565
|
setProxyCacheSize,
|
|
1826
|
-
lastPathRef
|
|
2566
|
+
lastPathRef,
|
|
2567
|
+
pluginDir
|
|
1827
2568
|
);
|
|
1828
2569
|
const server = createServer(handler);
|
|
1829
2570
|
const wss = new WebSocketServer({ server });
|
|
@@ -1855,9 +2596,9 @@ var DevUI = ({
|
|
|
1855
2596
|
server.close();
|
|
1856
2597
|
};
|
|
1857
2598
|
});
|
|
1858
|
-
}, [port, createServerLog, createProxyLog, broadcast, outputDir]);
|
|
2599
|
+
}, [port, createServerLog, createProxyLog, broadcast, outputDir, pluginDir]);
|
|
1859
2600
|
if (credMode === "editing") {
|
|
1860
|
-
return /* @__PURE__ */
|
|
2601
|
+
return /* @__PURE__ */ jsx7(
|
|
1861
2602
|
CredentialSetupUI,
|
|
1862
2603
|
{
|
|
1863
2604
|
...credentialMode !== void 0 && { initialMode: credentialMode },
|
|
@@ -1871,80 +2612,80 @@ var DevUI = ({
|
|
|
1871
2612
|
}
|
|
1872
2613
|
const elapsedSeconds = lastBuilt !== null ? Math.round((Date.now() - lastBuilt.getTime()) / 1e3) : null;
|
|
1873
2614
|
const elapsedLabel = elapsedSeconds === null ? "" : elapsedSeconds < 5 ? " (just now)" : ` (${String(elapsedSeconds)}s ago)`;
|
|
1874
|
-
return /* @__PURE__ */
|
|
1875
|
-
/* @__PURE__ */
|
|
1876
|
-
/* @__PURE__ */
|
|
1877
|
-
/* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
1879
|
-
buildStatus === "pending" && /* @__PURE__ */
|
|
1880
|
-
buildStatus === "building" && /* @__PURE__ */
|
|
1881
|
-
/* @__PURE__ */
|
|
1882
|
-
/* @__PURE__ */
|
|
2615
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", paddingY: 1, paddingX: 2, children: [
|
|
2616
|
+
/* @__PURE__ */ jsx7(AppHeader, { marginBottom: 1 }),
|
|
2617
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
2618
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 2, marginBottom: 0, children: [
|
|
2619
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, dimColor: true, children: "App Build" }),
|
|
2620
|
+
buildStatus === "pending" && /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "waiting\u2026" }),
|
|
2621
|
+
buildStatus === "building" && /* @__PURE__ */ jsxs5(Box6, { gap: 1, children: [
|
|
2622
|
+
/* @__PURE__ */ jsx7(Spinner, {}),
|
|
2623
|
+
/* @__PURE__ */ jsx7(Text7, { children: "Building plugin package\u2026" })
|
|
1883
2624
|
] }),
|
|
1884
|
-
buildStatus === "done" && /* @__PURE__ */
|
|
1885
|
-
/* @__PURE__ */
|
|
1886
|
-
lastBuilt !== null && /* @__PURE__ */
|
|
2625
|
+
buildStatus === "done" && /* @__PURE__ */ jsxs5(Box6, { gap: 1, children: [
|
|
2626
|
+
/* @__PURE__ */ jsx7(Text7, { color: "green", children: "\u2713 Built" }),
|
|
2627
|
+
lastBuilt !== null && /* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
1887
2628
|
"at ",
|
|
1888
2629
|
lastBuilt.toLocaleTimeString(),
|
|
1889
2630
|
elapsedLabel
|
|
1890
2631
|
] }),
|
|
1891
|
-
lastBundleSize !== null && /* @__PURE__ */
|
|
2632
|
+
lastBundleSize !== null && /* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
1892
2633
|
"\xB7 bundle: ",
|
|
1893
2634
|
formatBytes(lastBundleSize)
|
|
1894
2635
|
] })
|
|
1895
2636
|
] }),
|
|
1896
|
-
buildStatus === "error" && /* @__PURE__ */
|
|
2637
|
+
buildStatus === "error" && /* @__PURE__ */ jsxs5(Text7, { color: "red", children: [
|
|
1897
2638
|
"\u2717 ",
|
|
1898
2639
|
buildError ?? "unknown error"
|
|
1899
2640
|
] })
|
|
1900
2641
|
] }),
|
|
1901
|
-
/* @__PURE__ */
|
|
1902
|
-
|
|
2642
|
+
/* @__PURE__ */ jsx7(
|
|
2643
|
+
Box6,
|
|
1903
2644
|
{
|
|
1904
2645
|
flexDirection: "column",
|
|
1905
2646
|
height: LOG_DISPLAY + 2,
|
|
1906
2647
|
borderStyle: "single",
|
|
1907
2648
|
borderColor: "gray",
|
|
1908
2649
|
overflow: "hidden",
|
|
1909
|
-
children: buildLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */
|
|
2650
|
+
children: buildLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate", children: log }, index))
|
|
1910
2651
|
}
|
|
1911
2652
|
)
|
|
1912
2653
|
] }),
|
|
1913
|
-
/* @__PURE__ */
|
|
1914
|
-
/* @__PURE__ */
|
|
1915
|
-
/* @__PURE__ */
|
|
1916
|
-
status === "starting" && /* @__PURE__ */
|
|
1917
|
-
status === "running" && /* @__PURE__ */
|
|
1918
|
-
/* @__PURE__ */
|
|
1919
|
-
/* @__PURE__ */
|
|
2654
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
2655
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 2, children: [
|
|
2656
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, dimColor: true, children: "Web Server" }),
|
|
2657
|
+
status === "starting" && /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "starting\u2026" }),
|
|
2658
|
+
status === "running" && /* @__PURE__ */ jsxs5(Box6, { gap: 2, children: [
|
|
2659
|
+
/* @__PURE__ */ jsx7(Text7, { color: "green", children: "\u2713 Running" }),
|
|
2660
|
+
/* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
1920
2661
|
"\u25CF ",
|
|
1921
2662
|
wsClientCount,
|
|
1922
2663
|
" viewer",
|
|
1923
2664
|
wsClientCount !== 1 ? "s" : ""
|
|
1924
2665
|
] })
|
|
1925
2666
|
] }),
|
|
1926
|
-
status === "error" && /* @__PURE__ */
|
|
2667
|
+
status === "error" && /* @__PURE__ */ jsxs5(Text7, { color: "red", children: [
|
|
1927
2668
|
"\u2717 ",
|
|
1928
2669
|
errorMessage ?? "Server error"
|
|
1929
2670
|
] })
|
|
1930
2671
|
] }),
|
|
1931
|
-
/* @__PURE__ */
|
|
1932
|
-
|
|
2672
|
+
/* @__PURE__ */ jsx7(
|
|
2673
|
+
Box6,
|
|
1933
2674
|
{
|
|
1934
2675
|
flexDirection: "column",
|
|
1935
2676
|
height: LOG_DISPLAY + 2,
|
|
1936
2677
|
borderStyle: "single",
|
|
1937
2678
|
borderColor: "gray",
|
|
1938
2679
|
overflow: "hidden",
|
|
1939
|
-
children: serverLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */
|
|
2680
|
+
children: serverLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate", children: log }, index))
|
|
1940
2681
|
}
|
|
1941
2682
|
)
|
|
1942
2683
|
] }),
|
|
1943
|
-
/* @__PURE__ */
|
|
1944
|
-
/* @__PURE__ */
|
|
1945
|
-
/* @__PURE__ */
|
|
1946
|
-
/* @__PURE__ */
|
|
1947
|
-
/* @__PURE__ */
|
|
2684
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
2685
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 2, children: [
|
|
2686
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, dimColor: true, children: "Network Proxy" }),
|
|
2687
|
+
/* @__PURE__ */ jsx7(Text7, { color: "green", children: "\u2713 Active" }),
|
|
2688
|
+
/* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
1948
2689
|
"\u25CF ",
|
|
1949
2690
|
proxyCacheSize,
|
|
1950
2691
|
" key",
|
|
@@ -1957,26 +2698,47 @@ var DevUI = ({
|
|
|
1957
2698
|
" miss"
|
|
1958
2699
|
] })
|
|
1959
2700
|
] }),
|
|
1960
|
-
/* @__PURE__ */
|
|
1961
|
-
|
|
2701
|
+
/* @__PURE__ */ jsx7(
|
|
2702
|
+
Box6,
|
|
1962
2703
|
{
|
|
1963
2704
|
flexDirection: "column",
|
|
1964
2705
|
height: LOG_DISPLAY + 2,
|
|
1965
2706
|
borderStyle: "single",
|
|
1966
2707
|
borderColor: "gray",
|
|
1967
2708
|
overflow: "hidden",
|
|
1968
|
-
children: proxyLogHistory.length === 0 ? /* @__PURE__ */
|
|
2709
|
+
children: proxyLogHistory.length === 0 ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " No requests yet." }) : proxyLogHistory.slice(-LOG_DISPLAY).map((log, i) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate", children: log }, i))
|
|
1969
2710
|
}
|
|
1970
2711
|
)
|
|
1971
2712
|
] }),
|
|
1972
|
-
/* @__PURE__ */
|
|
1973
|
-
/* @__PURE__ */
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
2713
|
+
debug && /* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
2714
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 2, children: [
|
|
2715
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, dimColor: true, children: "CDP Debug" }),
|
|
2716
|
+
/* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
2717
|
+
"\u25CF ",
|
|
2718
|
+
cdpLogHistory.length,
|
|
2719
|
+
" event(s)"
|
|
2720
|
+
] })
|
|
2721
|
+
] }),
|
|
2722
|
+
/* @__PURE__ */ jsx7(
|
|
2723
|
+
Box6,
|
|
2724
|
+
{
|
|
2725
|
+
flexDirection: "column",
|
|
2726
|
+
height: LOG_DISPLAY + 2,
|
|
2727
|
+
borderStyle: "single",
|
|
2728
|
+
borderColor: "gray",
|
|
2729
|
+
overflow: "hidden",
|
|
2730
|
+
children: cdpLogHistory.length === 0 ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " No CDP events yet." }) : cdpLogHistory.slice(-LOG_DISPLAY).map((log, i) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate", children: log }, i))
|
|
2731
|
+
}
|
|
2732
|
+
)
|
|
2733
|
+
] }),
|
|
2734
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, gap: 1, children: [
|
|
2735
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "[" }),
|
|
2736
|
+
/* @__PURE__ */ jsx7(Text7, { children: "q" }),
|
|
2737
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "] quit [" }),
|
|
2738
|
+
/* @__PURE__ */ jsx7(Text7, { children: "c" }),
|
|
2739
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "] credentials [" }),
|
|
2740
|
+
/* @__PURE__ */ jsx7(Text7, { children: "v" }),
|
|
2741
|
+
/* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
1980
2742
|
"] ",
|
|
1981
2743
|
wsClientCount === 0 ? "open viewer" : "relaunch viewer"
|
|
1982
2744
|
] })
|
|
@@ -1986,14 +2748,14 @@ var DevUI = ({
|
|
|
1986
2748
|
|
|
1987
2749
|
// src/commands/dev.ts
|
|
1988
2750
|
async function runSetupUI(initialMode) {
|
|
1989
|
-
return new Promise((
|
|
2751
|
+
return new Promise((resolve2) => {
|
|
1990
2752
|
let unmountFn = null;
|
|
1991
2753
|
const handleComplete = (result) => {
|
|
1992
2754
|
unmountFn?.();
|
|
1993
|
-
|
|
2755
|
+
resolve2(result);
|
|
1994
2756
|
};
|
|
1995
|
-
const { unmount } =
|
|
1996
|
-
|
|
2757
|
+
const { unmount } = render3(
|
|
2758
|
+
createElement3(CredentialSetupUI, {
|
|
1997
2759
|
...initialMode !== void 0 && { initialMode },
|
|
1998
2760
|
onComplete: handleComplete
|
|
1999
2761
|
}),
|
|
@@ -2003,68 +2765,75 @@ async function runSetupUI(initialMode) {
|
|
|
2003
2765
|
});
|
|
2004
2766
|
}
|
|
2005
2767
|
function devCommand(program2) {
|
|
2006
|
-
program2.command("dev").description("Start the plugin viewer dev server").option("-p, --port <port>", "port to listen on", "3121").option("-c, --credentials <path>", "path to a credentials JSON file").
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
credentialMode
|
|
2021
|
-
|
|
2022
|
-
credentialMode
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2768
|
+
program2.command("dev").description("Start the plugin viewer dev server").option("-p, --port <port>", "port to listen on", "3121").option("-c, --credentials <path>", "path to a credentials JSON file").option("-d, --debug", "Enable CDP lifecycle logs in the TUI").option("-v, --verbose", "Log every CDP event and handled error (implies --debug)").action(
|
|
2769
|
+
async (options) => {
|
|
2770
|
+
const port = parseInt(options.port, 10);
|
|
2771
|
+
const pluginDir = process.cwd();
|
|
2772
|
+
const outputDir = `${pluginDir}/.kizenapp`;
|
|
2773
|
+
ensureGitignore(pluginDir);
|
|
2774
|
+
let credentials = null;
|
|
2775
|
+
let credentialMode;
|
|
2776
|
+
let activeCredentialProfile;
|
|
2777
|
+
const { lastPath } = await loadConfig(outputDir);
|
|
2778
|
+
if (options.credentials) {
|
|
2779
|
+
credentials = await loadCredentialsFromFile(options.credentials);
|
|
2780
|
+
} else {
|
|
2781
|
+
const config = await loadConfig(outputDir);
|
|
2782
|
+
if (config.credentialMode === "local") {
|
|
2783
|
+
credentialMode = "local";
|
|
2784
|
+
} else if (config.credentialMode === "global") {
|
|
2785
|
+
credentialMode = "global";
|
|
2786
|
+
activeCredentialProfile = config.activeCredentialProfile;
|
|
2787
|
+
if (activeCredentialProfile) {
|
|
2788
|
+
credentials = await loadCredentialProfile(activeCredentialProfile);
|
|
2789
|
+
if (!credentials) {
|
|
2790
|
+
credentials = await loadGlobalCredentials();
|
|
2791
|
+
activeCredentialProfile = void 0;
|
|
2792
|
+
}
|
|
2793
|
+
} else {
|
|
2027
2794
|
credentials = await loadGlobalCredentials();
|
|
2028
|
-
|
|
2795
|
+
}
|
|
2796
|
+
if (!credentials) {
|
|
2797
|
+
const result = await runSetupUI("global");
|
|
2798
|
+
credentials = result.credentials;
|
|
2799
|
+
activeCredentialProfile = result.profileName;
|
|
2029
2800
|
}
|
|
2030
2801
|
} else {
|
|
2031
|
-
|
|
2032
|
-
}
|
|
2033
|
-
if (!credentials) {
|
|
2034
|
-
const result = await runSetupUI("global");
|
|
2802
|
+
const result = await runSetupUI();
|
|
2035
2803
|
credentials = result.credentials;
|
|
2804
|
+
credentialMode = result.mode;
|
|
2036
2805
|
activeCredentialProfile = result.profileName;
|
|
2806
|
+
const profile = result.profileName;
|
|
2807
|
+
await saveConfig(outputDir, {
|
|
2808
|
+
credentialMode: result.mode,
|
|
2809
|
+
...profile !== void 0 && { activeCredentialProfile: profile }
|
|
2810
|
+
});
|
|
2037
2811
|
}
|
|
2038
|
-
} else {
|
|
2039
|
-
const result = await runSetupUI();
|
|
2040
|
-
credentials = result.credentials;
|
|
2041
|
-
credentialMode = result.mode;
|
|
2042
|
-
activeCredentialProfile = result.profileName;
|
|
2043
|
-
const profile = result.profileName;
|
|
2044
|
-
await saveConfig(outputDir, {
|
|
2045
|
-
credentialMode: result.mode,
|
|
2046
|
-
...profile !== void 0 && { activeCredentialProfile: profile }
|
|
2047
|
-
});
|
|
2048
2812
|
}
|
|
2813
|
+
const verbose = options.verbose === true;
|
|
2814
|
+
const debug = verbose || options.debug === true;
|
|
2815
|
+
const { waitUntilExit } = render3(
|
|
2816
|
+
createElement3(DevUI, {
|
|
2817
|
+
port,
|
|
2818
|
+
pluginDir,
|
|
2819
|
+
outputDir,
|
|
2820
|
+
credentials,
|
|
2821
|
+
...credentialMode !== void 0 && { credentialMode },
|
|
2822
|
+
...activeCredentialProfile !== void 0 && { activeCredentialProfile },
|
|
2823
|
+
...lastPath !== void 0 && { lastPath },
|
|
2824
|
+
debug,
|
|
2825
|
+
verbose
|
|
2826
|
+
}),
|
|
2827
|
+
{ exitOnCtrlC: false }
|
|
2828
|
+
);
|
|
2829
|
+
await waitUntilExit();
|
|
2049
2830
|
}
|
|
2050
|
-
|
|
2051
|
-
createElement2(DevUI, {
|
|
2052
|
-
port,
|
|
2053
|
-
pluginDir,
|
|
2054
|
-
outputDir,
|
|
2055
|
-
credentials,
|
|
2056
|
-
...credentialMode !== void 0 && { credentialMode },
|
|
2057
|
-
...activeCredentialProfile !== void 0 && { activeCredentialProfile },
|
|
2058
|
-
...lastPath !== void 0 && { lastPath }
|
|
2059
|
-
}),
|
|
2060
|
-
{ exitOnCtrlC: false }
|
|
2061
|
-
);
|
|
2062
|
-
await waitUntilExit();
|
|
2063
|
-
});
|
|
2831
|
+
);
|
|
2064
2832
|
}
|
|
2065
2833
|
|
|
2066
2834
|
// src/index.ts
|
|
2067
2835
|
program.name("appbuilder").description("Kizen plugin app builder").version("0.1.0");
|
|
2836
|
+
createCommand(program);
|
|
2068
2837
|
buildCommand(program);
|
|
2069
2838
|
devCommand(program);
|
|
2070
2839
|
program.parse();
|