@kizenapps/cli 0.13.0 → 0.14.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/LICENSE.md +674 -0
- package/README.md +42 -0
- package/dist/index.js +13 -2679
- package/dist/index.js.map +1 -1
- package/dist/viewer/assets/{AppDetailPage-Ck_VyLCS.js → AppDetailPage-D4r3W5v5.js} +1 -1
- package/dist/viewer/assets/{BootstrapContext-CL_onTG7.js → BootstrapContext-D5ZnMrOp.js} +1 -1
- package/dist/viewer/assets/CodeStepsPage-BzaaN9Kb.js +1 -0
- package/dist/viewer/assets/ConfigurationPage-DyGQQTe-.js +1 -0
- package/dist/viewer/assets/DevSidebar-h9gYdctz.js +1 -0
- package/dist/viewer/assets/IconReferencePage-BLciUKH9.js +1 -0
- package/dist/viewer/assets/SandboxPage-IYn-KIbv.js +1 -0
- package/dist/viewer/assets/{SourceBrowserPage-Cr6XOIdV.js → SourceBrowserPage-BOlm36Dg.js} +1 -1
- package/dist/viewer/assets/Tooltip-tpqNw0op.js +1 -0
- package/dist/viewer/assets/WhenBadge-GxhTQKsq.js +1 -0
- package/dist/viewer/assets/api-C5Iu-4AS.js +1 -0
- package/dist/viewer/assets/configStorage-BnMh081m.js +10 -0
- package/dist/viewer/assets/index-CjU7OUke.js +5 -0
- package/dist/viewer/assets/index-dW-Omils.css +2 -0
- package/dist/viewer/assets/storageKeys-ClzdCxlg.js +1 -0
- package/dist/viewer/assets/useCriticalExceptionDialog-DK8ufamu.js +5 -0
- package/dist/viewer/index.html +5 -5
- package/package.json +3 -2
- package/dist/chunk-4Z6S6LWP.js +0 -6134
- package/dist/chunk-4Z6S6LWP.js.map +0 -1
- package/dist/chunk-HJPLDI45.js +0 -6134
- package/dist/chunk-HJPLDI45.js.map +0 -1
- package/dist/chunk-LSHHXCC6.js +0 -3639
- package/dist/chunk-LSHHXCC6.js.map +0 -1
- package/dist/chunk-SEGVTWSK.js +0 -44
- package/dist/chunk-SEGVTWSK.js.map +0 -1
- package/dist/chunk-ZWE3DS7E.js +0 -39
- package/dist/chunk-ZWE3DS7E.js.map +0 -1
- package/dist/devtools-LZRAH4XS.js +0 -90
- package/dist/devtools-LZRAH4XS.js.map +0 -1
- package/dist/dist-C3F6UOWF.js +0 -8472
- package/dist/dist-C3F6UOWF.js.map +0 -1
- package/dist/dist-NUJOSN7W.js +0 -8472
- package/dist/dist-NUJOSN7W.js.map +0 -1
- package/dist/dist-QHDMIBVE.js +0 -135
- package/dist/dist-QHDMIBVE.js.map +0 -1
- package/dist/dist-ZIZQOSY3.js +0 -135
- package/dist/dist-ZIZQOSY3.js.map +0 -1
- package/dist/gzip-size-K37OQCQ2.js +0 -137
- package/dist/gzip-size-K37OQCQ2.js.map +0 -1
- package/dist/gzip-size-QRGIYYEQ.js +0 -137
- package/dist/gzip-size-QRGIYYEQ.js.map +0 -1
- package/dist/viewer/assets/AppDetailPage-Bh4FLKqJ.js +0 -1
- package/dist/viewer/assets/AppDetailPage-BoFB93TE.js +0 -1
- package/dist/viewer/assets/BootstrapContext-c_1rWAfc.js +0 -1
- package/dist/viewer/assets/Card-C7KVwKfm.js +0 -1
- package/dist/viewer/assets/CodeStepsPage-BHbbvjvr.js +0 -1
- package/dist/viewer/assets/CodeStepsPage-DnjVkPGB.js +0 -1
- package/dist/viewer/assets/CodeStepsPage-vHoGY4qA.js +0 -1
- package/dist/viewer/assets/CodeViewer--wbTev7Q.js +0 -4
- package/dist/viewer/assets/ConfigurationPage-DAVSSfuO.js +0 -1
- package/dist/viewer/assets/ConfigurationPage-_PScSEXP.js +0 -1
- package/dist/viewer/assets/ConfigurationPage-zSJDg1f4.js +0 -1
- package/dist/viewer/assets/CredentialsContext-Rkt9WDIb.js +0 -1
- package/dist/viewer/assets/CredentialsContext-k-q4M-VC.js +0 -1
- package/dist/viewer/assets/DevSidebar-CKdTotWS.js +0 -1
- package/dist/viewer/assets/DevSidebar-CcSTChgY.js +0 -1
- package/dist/viewer/assets/IconReferencePage-BhQv3kD4.js +0 -1
- package/dist/viewer/assets/IconReferencePage-C_I2Fxnr.js +0 -1
- package/dist/viewer/assets/PluginIcon-CWkWM-Eu.js +0 -1
- package/dist/viewer/assets/SandboxPage-BMz04QVs.js +0 -1
- package/dist/viewer/assets/SandboxPage-BWtQb8kY.js +0 -1
- package/dist/viewer/assets/SandboxPage-DOBUc8VL.js +0 -1
- package/dist/viewer/assets/SidebarContext-DMhpPBf_.js +0 -1
- package/dist/viewer/assets/SourceBrowserPage-C-IhTiL0.js +0 -1
- package/dist/viewer/assets/VersionsPage-Bpj4_jPC.js +0 -1
- package/dist/viewer/assets/VersionsPage-T8tsqgQx.js +0 -1
- package/dist/viewer/assets/api-DFDquUsn.js +0 -1
- package/dist/viewer/assets/api-bszpvP9L.js +0 -1
- package/dist/viewer/assets/bundleQuery-BQLpZ06V.js +0 -1
- package/dist/viewer/assets/fa-core-C4eeAd4v.js +0 -558
- package/dist/viewer/assets/fa-pro-icons-Dr3O0gTt.js +0 -1
- package/dist/viewer/assets/formatBytes-BCP0hIUu.js +0 -1
- package/dist/viewer/assets/index-7CwR8Ogd.js +0 -5
- package/dist/viewer/assets/index-CCB9tECd.css +0 -2
- package/dist/viewer/assets/index-FEIsXxbf.js +0 -5
- package/dist/viewer/assets/index-fU82pXSW.js +0 -5
- package/dist/viewer/assets/react-CmC5kGa_.js +0 -9
- package/dist/viewer/assets/useCriticalExceptionDialog-BeVl0uaO.js +0 -14
- package/dist/viewer/assets/useCriticalExceptionDialog-DseJ_u9B.js +0 -14
- package/dist/viewer/assets/useCurrentApp-CWtSEVC4.js +0 -1
- package/dist/viewer/assets/useCurrentApp-x1p8URhM.js +0 -1
- package/dist/viewer/assets/useLocalStorage-HYhrPUxS.js +0 -1
- package/dist/viewer/assets/useNavigate-Ci7hClRo.js +0 -1
- package/dist/viewer/assets/useQuery-B3Ek2Yet.js +0 -1
- package/dist/viewer/assets/validIcons-CzxkK9y8.js +0 -1
- /package/dist/viewer/assets/{CodeViewer-Cm7Th9pG.js → CodeViewer-Ce2qKSWi.js} +0 -0
- /package/dist/viewer/assets/{VersionsPage-DAm4Ozs0.js → VersionsPage-C02dpN3Y.js} +0 -0
- /package/dist/viewer/assets/{useLocalStorage-eJ1PxgAP.js → useLocalStorage-B73GyZbK.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,863 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { render } from "ink";
|
|
9
|
-
|
|
10
|
-
// src/ui/BuildUI.tsx
|
|
11
|
-
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
12
|
-
import { Box as Box3, Text as Text4, useApp } from "ink";
|
|
13
|
-
|
|
14
|
-
// src/lib/runBuild.ts
|
|
15
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
16
|
-
import { join as join2 } from "path";
|
|
17
|
-
import { minifyFiles, packagePlugin, transformDeployablePlugin } from "@kizenapps/packager";
|
|
18
|
-
|
|
19
|
-
// src/lib/readFiles.ts
|
|
20
|
-
import { readdir, readFile } from "fs/promises";
|
|
21
|
-
import { join, relative } from "path";
|
|
22
|
-
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".svg"]);
|
|
23
|
-
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([".kzn"]);
|
|
24
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".kizenapp", ".github"]);
|
|
25
|
-
async function walk(dir, rootDir) {
|
|
26
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
27
|
-
const paths = [];
|
|
28
|
-
for (const entry of entries) {
|
|
29
|
-
if (entry.isDirectory()) {
|
|
30
|
-
if (SKIP_DIRS.has(entry.name)) {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
paths.push(...await walk(join(dir, entry.name), rootDir));
|
|
34
|
-
} else if (entry.isFile()) {
|
|
35
|
-
paths.push(join(dir, entry.name));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return paths;
|
|
39
|
-
}
|
|
40
|
-
async function readLocalFiles(rootDir) {
|
|
41
|
-
const absolutePaths = await walk(rootDir, rootDir);
|
|
42
|
-
return Promise.all(
|
|
43
|
-
absolutePaths.map(async (absPath) => {
|
|
44
|
-
const relPath = relative(rootDir, absPath).split("\\").join("/");
|
|
45
|
-
const dotIndex = relPath.lastIndexOf(".");
|
|
46
|
-
const ext = dotIndex >= 0 ? relPath.slice(dotIndex).toLowerCase() : "";
|
|
47
|
-
if (IMAGE_EXTENSIONS.has(ext)) {
|
|
48
|
-
const buf = await readFile(absPath);
|
|
49
|
-
return { path: relPath, content: "", base64Image: buf.toString("base64") };
|
|
50
|
-
}
|
|
51
|
-
if (BINARY_EXTENSIONS.has(ext)) {
|
|
52
|
-
const buf = await readFile(absPath);
|
|
53
|
-
return { path: relPath, content: "", binaryData: buf };
|
|
54
|
-
}
|
|
55
|
-
const content = await readFile(absPath, "utf-8");
|
|
56
|
-
return { path: relPath, content };
|
|
57
|
-
})
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// src/lib/runBuild.ts
|
|
62
|
-
var serializePlugin = (plugin, allReleaseNotes) => ({
|
|
63
|
-
...plugin,
|
|
64
|
-
thumbnail: plugin.thumbnail ? Buffer.from(plugin.thumbnail).toString("base64") : null,
|
|
65
|
-
kznFile: plugin.kznFile ? Buffer.from(plugin.kznFile).toString("base64") : null,
|
|
66
|
-
allReleaseNotes
|
|
67
|
-
});
|
|
68
|
-
async function runBuild(pluginDir, outputDir, onStep) {
|
|
69
|
-
await mkdir(outputDir, { recursive: true });
|
|
70
|
-
onStep?.("reading-files");
|
|
71
|
-
const files = await readLocalFiles(pluginDir);
|
|
72
|
-
onStep?.("minifying");
|
|
73
|
-
const minified = await minifyFiles(files);
|
|
74
|
-
onStep?.("packaging");
|
|
75
|
-
const manifestFile = minified.find((f) => f.path === "kizen.json");
|
|
76
|
-
if (!manifestFile) {
|
|
77
|
-
throw new Error("kizen.json not found in plugin directory.");
|
|
78
|
-
}
|
|
79
|
-
const manifests = JSON.parse(manifestFile.content);
|
|
80
|
-
const packaged = packagePlugin(minified, manifests);
|
|
81
|
-
const deployable = Object.values(packaged).map(transformDeployablePlugin);
|
|
82
|
-
onStep?.("writing-bundle");
|
|
83
|
-
const bundle = deployable.map((plugin) => {
|
|
84
|
-
const notesDir = plugin.release_notes_directory?.replace(/\/$/, "");
|
|
85
|
-
const allReleaseNotes = notesDir ? files.filter((f) => f.path.startsWith(notesDir + "/") && f.path.endsWith(".md") && f.content).map((f) => ({
|
|
86
|
-
version: f.path.slice(f.path.lastIndexOf("/") + 1, -3),
|
|
87
|
-
notes: f.content
|
|
88
|
-
})).sort((a, b) => b.version.localeCompare(a.version, void 0, { numeric: true })) : [];
|
|
89
|
-
return serializePlugin(plugin, allReleaseNotes);
|
|
90
|
-
});
|
|
91
|
-
const jsonStr = JSON.stringify(bundle, null, 2);
|
|
92
|
-
const sizedBundle = bundle.map(({ allReleaseNotes: _notes, ...rest }) => rest);
|
|
93
|
-
const bundleSize = Buffer.byteLength(JSON.stringify(sizedBundle, null, 2), "utf-8");
|
|
94
|
-
await writeFile(join2(outputDir, "bundle.json"), jsonStr, "utf-8");
|
|
95
|
-
return { bundleSize };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// src/lib/formatBytes.ts
|
|
99
|
-
function formatBytes(n) {
|
|
100
|
-
if (n < 1024) {
|
|
101
|
-
return `${String(n)} B`;
|
|
102
|
-
}
|
|
103
|
-
if (n < 1024 * 1024) {
|
|
104
|
-
return `${(n / 1024).toFixed(1)} KB`;
|
|
105
|
-
}
|
|
106
|
-
return `${(n / (1024 * 1024)).toFixed(2)} MB`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// src/ui/AppHeader.tsx
|
|
110
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
111
|
-
|
|
112
|
-
// src/ui/Logo.tsx
|
|
113
|
-
import { Box, Text } from "ink";
|
|
114
|
-
import { jsx } from "react/jsx-runtime";
|
|
115
|
-
var LOGO_LINES = [
|
|
116
|
-
" :::::::: ",
|
|
117
|
-
" :::::: ::::::::::: ",
|
|
118
|
-
" :::::: :::::::::: ",
|
|
119
|
-
" :::::: :::::: ::::::: ",
|
|
120
|
-
" ::::: :::::: ::::::::::::::::: ",
|
|
121
|
-
" ::::: ::::: ::::::::::::::::::: ",
|
|
122
|
-
" ::::: ::::: :::::: ::::: ",
|
|
123
|
-
":::: ::::: ::: ::::: :: ",
|
|
124
|
-
"::::: :::::: :::::::::: ",
|
|
125
|
-
"::::: :::::: ::::::::::: ",
|
|
126
|
-
" ::::: ::: ::::::: ",
|
|
127
|
-
" ::::::: ::: ::::: ",
|
|
128
|
-
" ::::::::::: :::::: :::::",
|
|
129
|
-
" :::::::::: :::::: :::::",
|
|
130
|
-
" :: ::::: ::: ::::: ::::",
|
|
131
|
-
" ::::: :::::: ::::: ::::: ::: ",
|
|
132
|
-
" ::::::::::::::::::: ::::: ::::: ",
|
|
133
|
-
" ::::::::::::::::: ::::: ::::: ",
|
|
134
|
-
" ::::::::: :::::: :::::: ",
|
|
135
|
-
" :::::::::: :::::: ",
|
|
136
|
-
" ::::::::::: :::::: ",
|
|
137
|
-
" :::::::: "
|
|
138
|
-
];
|
|
139
|
-
var Logo = () => /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: LOGO_LINES.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "cyan", children: line }, i)) });
|
|
140
|
-
|
|
141
|
-
// src/ui/AppHeader.tsx
|
|
142
|
-
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
143
|
-
var AppHeader = ({ marginBottom = 0 }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
144
|
-
/* @__PURE__ */ jsx2(Logo, {}),
|
|
145
|
-
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, marginBottom, children: /* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "Kizen App Development Toolkit" }) })
|
|
146
|
-
] });
|
|
147
|
-
|
|
148
|
-
// src/ui/Spinner.tsx
|
|
149
|
-
import { useEffect, useState } from "react";
|
|
150
|
-
import { Text as Text3 } from "ink";
|
|
151
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
152
|
-
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
153
|
-
var Spinner = () => {
|
|
154
|
-
const [frame, setFrame] = useState(0);
|
|
155
|
-
useEffect(() => {
|
|
156
|
-
const id = setInterval(() => {
|
|
157
|
-
setFrame((prev) => (prev + 1) % SPINNER_FRAMES.length);
|
|
158
|
-
}, 80);
|
|
159
|
-
return () => {
|
|
160
|
-
clearInterval(id);
|
|
161
|
-
};
|
|
162
|
-
}, []);
|
|
163
|
-
return /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: SPINNER_FRAMES[frame] ?? "\u280B" });
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// src/ui/BuildUI.tsx
|
|
167
|
-
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
168
|
-
var STEPS = [
|
|
169
|
-
"creating-dir",
|
|
170
|
-
"reading-files",
|
|
171
|
-
"minifying",
|
|
172
|
-
"packaging",
|
|
173
|
-
"writing-bundle"
|
|
174
|
-
];
|
|
175
|
-
var STEP_LABELS = {
|
|
176
|
-
"creating-dir": "Creating .kizenapp directory",
|
|
177
|
-
"reading-files": "Reading plugin files",
|
|
178
|
-
minifying: "Minifying scripts",
|
|
179
|
-
packaging: "Packaging plugin",
|
|
180
|
-
"writing-bundle": "Writing bundle.json"
|
|
181
|
-
};
|
|
182
|
-
var BuildUI = ({ outputDir, pluginDir }) => {
|
|
183
|
-
const app = useApp();
|
|
184
|
-
const [step, setStep] = useState2("creating-dir");
|
|
185
|
-
const [errorMessage, setErrorMessage] = useState2(null);
|
|
186
|
-
const [bundleSize, setBundleSize] = useState2(null);
|
|
187
|
-
useEffect2(() => {
|
|
188
|
-
void runBuild(pluginDir, outputDir, setStep).then(({ bundleSize: size }) => {
|
|
189
|
-
setBundleSize(size);
|
|
190
|
-
setStep("done");
|
|
191
|
-
app.exit();
|
|
192
|
-
}).catch((err) => {
|
|
193
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
194
|
-
setErrorMessage(message);
|
|
195
|
-
setStep("error");
|
|
196
|
-
app.exit();
|
|
197
|
-
});
|
|
198
|
-
}, [outputDir, pluginDir, app]);
|
|
199
|
-
const currentIndex = STEPS.indexOf(step);
|
|
200
|
-
const isError = step === "error";
|
|
201
|
-
const isDone = step === "done";
|
|
202
|
-
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingY: 1, paddingX: 2, children: [
|
|
203
|
-
/* @__PURE__ */ jsx4(AppHeader, { marginBottom: 1 }),
|
|
204
|
-
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
|
|
205
|
-
STEPS.map((s, i) => {
|
|
206
|
-
const isActive = step === s;
|
|
207
|
-
const isStepDone = isDone || currentIndex > i;
|
|
208
|
-
const isFailed = isError && i === currentIndex;
|
|
209
|
-
return /* @__PURE__ */ jsx4(Box3, { gap: 1, children: isFailed ? /* @__PURE__ */ jsxs2(Text4, { color: "red", children: [
|
|
210
|
-
"\u2717 ",
|
|
211
|
-
STEP_LABELS[s]
|
|
212
|
-
] }) : isStepDone ? /* @__PURE__ */ jsxs2(Text4, { color: "green", children: [
|
|
213
|
-
"\u2713 ",
|
|
214
|
-
STEP_LABELS[s]
|
|
215
|
-
] }) : isActive ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
216
|
-
/* @__PURE__ */ jsx4(Spinner, {}),
|
|
217
|
-
/* @__PURE__ */ jsx4(Text4, { children: STEP_LABELS[s] })
|
|
218
|
-
] }) : /* @__PURE__ */ jsxs2(Text4, { dimColor: true, children: [
|
|
219
|
-
" ",
|
|
220
|
-
STEP_LABELS[s]
|
|
221
|
-
] }) }, s);
|
|
222
|
-
}),
|
|
223
|
-
isDone && bundleSize !== null && /* @__PURE__ */ jsx4(Box3, { marginTop: 1, gap: 1, children: /* @__PURE__ */ jsxs2(Text4, { color: "green", children: [
|
|
224
|
-
"\u2713 bundle \u2014 ",
|
|
225
|
-
formatBytes(bundleSize)
|
|
226
|
-
] }) }),
|
|
227
|
-
errorMessage !== null && /* @__PURE__ */ jsx4(Box3, { marginTop: 1, borderStyle: "single", borderColor: "red", paddingX: 1, children: /* @__PURE__ */ jsx4(Text4, { color: "red", children: errorMessage }) })
|
|
228
|
-
] })
|
|
229
|
-
] });
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
// src/lib/gitignore.ts
|
|
233
|
-
import * as fs from "fs";
|
|
234
|
-
import * as path from "path";
|
|
235
|
-
function ensureGitignore(projectDir) {
|
|
236
|
-
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
237
|
-
const entry = ".kizenapp/";
|
|
238
|
-
let contents = "";
|
|
239
|
-
if (fs.existsSync(gitignorePath)) {
|
|
240
|
-
contents = fs.readFileSync(gitignorePath, "utf8");
|
|
241
|
-
const lines = contents.split("\n").map((l) => l.trim());
|
|
242
|
-
if (lines.includes(entry) || lines.includes(".kizenapp")) {
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
const addition = contents.endsWith("\n") ? entry + "\n" : "\n" + entry + "\n";
|
|
247
|
-
fs.writeFileSync(gitignorePath, contents + addition);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// src/commands/build.ts
|
|
251
|
-
function buildCommand(program2) {
|
|
252
|
-
program2.command("build").description("Bundle the plugin app into .kizenapp directory").action(async () => {
|
|
253
|
-
const pluginDir = process.cwd();
|
|
254
|
-
const outputDir = `${pluginDir}/.kizenapp`;
|
|
255
|
-
ensureGitignore(pluginDir);
|
|
256
|
-
const { waitUntilExit } = render(createElement(BuildUI, { outputDir, pluginDir }));
|
|
257
|
-
await waitUntilExit();
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// src/commands/create.ts
|
|
262
|
-
import { createElement as createElement2 } from "react";
|
|
263
|
-
import { render as render2 } from "ink";
|
|
264
|
-
|
|
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";
|
|
269
|
-
|
|
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
|
|
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) {
|
|
349
|
-
return {
|
|
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;
|
|
375
|
-
}
|
|
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
|
|
405
|
-
});
|
|
406
|
-
setPhase({ type: "done", targetDir: dir });
|
|
407
|
-
} catch (err) {
|
|
408
|
-
setPhase({
|
|
409
|
-
type: "error",
|
|
410
|
-
message: err instanceof Error ? err.message : String(err)
|
|
411
|
-
});
|
|
412
|
-
}
|
|
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
|
-
] });
|
|
664
|
-
};
|
|
665
|
-
|
|
666
|
-
// src/lib/credentials.ts
|
|
667
|
-
import { mkdir as mkdir3, readFile as readFile2, readdir as readdir2, writeFile as writeFile3 } from "fs/promises";
|
|
668
|
-
import { homedir } from "os";
|
|
669
|
-
import { dirname, join as join6 } from "path";
|
|
670
|
-
var ENVIRONMENTS = ["go", "fmo", "staging", "integration", "test1"];
|
|
671
|
-
var GLOBAL_CREDENTIALS_DIR = join6(homedir(), ".kizenappbuilder");
|
|
672
|
-
var GLOBAL_CREDENTIALS_PATH = join6(GLOBAL_CREDENTIALS_DIR, "credentials.json");
|
|
673
|
-
var DEFAULT_PROFILE_NAME = "credentials";
|
|
674
|
-
function isValidEnvironment(value) {
|
|
675
|
-
return ENVIRONMENTS.includes(value);
|
|
676
|
-
}
|
|
677
|
-
function parseCredentials(raw) {
|
|
678
|
-
if (typeof raw !== "object" || raw === null) {
|
|
679
|
-
throw new Error("Credentials must be a JSON object");
|
|
680
|
-
}
|
|
681
|
-
const obj = raw;
|
|
682
|
-
const env = obj.environment;
|
|
683
|
-
return {
|
|
684
|
-
apiKey: typeof obj.apiKey === "string" ? obj.apiKey : "",
|
|
685
|
-
userId: typeof obj.userId === "string" ? obj.userId : "",
|
|
686
|
-
businessId: typeof obj.businessId === "string" ? obj.businessId : "",
|
|
687
|
-
environment: isValidEnvironment(env) ? env : "go"
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
async function loadCredentialsFromFile(filePath) {
|
|
691
|
-
const content = await readFile2(filePath, "utf-8");
|
|
692
|
-
return parseCredentials(JSON.parse(content));
|
|
693
|
-
}
|
|
694
|
-
async function loadGlobalCredentials() {
|
|
695
|
-
try {
|
|
696
|
-
return await loadCredentialsFromFile(GLOBAL_CREDENTIALS_PATH);
|
|
697
|
-
} catch {
|
|
698
|
-
return null;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
async function saveGlobalCredentials(credentials) {
|
|
702
|
-
await mkdir3(dirname(GLOBAL_CREDENTIALS_PATH), { recursive: true });
|
|
703
|
-
await writeFile3(GLOBAL_CREDENTIALS_PATH, JSON.stringify(credentials, null, 2), "utf-8");
|
|
704
|
-
}
|
|
705
|
-
function getProfilePath(name) {
|
|
706
|
-
return join6(GLOBAL_CREDENTIALS_DIR, `${name}.json`);
|
|
707
|
-
}
|
|
708
|
-
async function listCredentialProfiles() {
|
|
709
|
-
const profiles = [
|
|
710
|
-
{ name: DEFAULT_PROFILE_NAME, path: GLOBAL_CREDENTIALS_PATH, isDefault: true }
|
|
711
|
-
];
|
|
712
|
-
try {
|
|
713
|
-
const entries = await readdir2(GLOBAL_CREDENTIALS_DIR);
|
|
714
|
-
for (const entry of entries) {
|
|
715
|
-
if (!entry.endsWith(".json")) {
|
|
716
|
-
continue;
|
|
717
|
-
}
|
|
718
|
-
const name = entry.slice(0, -5);
|
|
719
|
-
if (name === DEFAULT_PROFILE_NAME) {
|
|
720
|
-
continue;
|
|
721
|
-
}
|
|
722
|
-
profiles.push({ name, path: join6(GLOBAL_CREDENTIALS_DIR, entry), isDefault: false });
|
|
723
|
-
}
|
|
724
|
-
} catch {
|
|
725
|
-
}
|
|
726
|
-
return profiles;
|
|
727
|
-
}
|
|
728
|
-
async function saveCredentialProfile(name, credentials) {
|
|
729
|
-
if (name === DEFAULT_PROFILE_NAME) {
|
|
730
|
-
await saveGlobalCredentials(credentials);
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
await mkdir3(GLOBAL_CREDENTIALS_DIR, { recursive: true });
|
|
734
|
-
await writeFile3(getProfilePath(name), JSON.stringify(credentials, null, 2), "utf-8");
|
|
735
|
-
}
|
|
736
|
-
async function loadCredentialProfile(name) {
|
|
737
|
-
try {
|
|
738
|
-
return await loadCredentialsFromFile(getProfilePath(name));
|
|
739
|
-
} catch {
|
|
740
|
-
return null;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
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
|
-
|
|
839
|
-
// src/lib/config.ts
|
|
840
|
-
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
841
|
-
import { join as join7 } from "path";
|
|
842
|
-
async function loadConfig(outputDir) {
|
|
843
|
-
try {
|
|
844
|
-
const content = await readFile3(join7(outputDir, "config.json"), "utf-8");
|
|
845
|
-
return JSON.parse(content);
|
|
846
|
-
} catch {
|
|
847
|
-
return {};
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
async function saveConfig(outputDir, config) {
|
|
851
|
-
await mkdir4(outputDir, { recursive: true });
|
|
852
|
-
await writeFile4(join7(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// src/server/pythonExecutor.ts
|
|
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";
|
|
859
|
-
import { tmpdir } from "os";
|
|
860
|
-
var WRAPPER_SCRIPT = `
|
|
2
|
+
import{readFileSync as go}from"fs";import{dirname as yo,join as ho}from"path";import{fileURLToPath as xo}from"url";import{program as Le}from"commander";import{createElement as Qr}from"react";import{render as en}from"ink";import{useEffect as Xr,useState as ct}from"react";import{Box as Pe,Text as me,useApp as Yr}from"ink";import{mkdir as Br,writeFile as Dr}from"fs/promises";import{join as Rr}from"path";import{minifyFiles as Nr,packagePlugin as Lr,transformDeployablePlugin as Fr}from"@kizenapps/packager";import{readdir as _r,readFile as at}from"fs/promises";import{join as Tt,relative as Er}from"path";var kr=new Set([".png",".svg"]),Ir=new Set([".kzn"]),$e=new Set(["node_modules",".git",".kizenapp",".github"]);async function _t(e,t){let i=await _r(e,{withFileTypes:!0}),n=[];for(let r of i)if(r.isDirectory()){if($e.has(r.name))continue;n.push(...await _t(Tt(e,r.name),t))}else r.isFile()&&n.push(Tt(e,r.name));return n}async function Et(e){let t=await _t(e,e);return Promise.all(t.map(async i=>{let n=Er(e,i).split("\\").join("/"),r=n.lastIndexOf("."),c=r>=0?n.slice(r).toLowerCase():"";if(kr.has(c)){let y=await at(i);return{path:n,content:"",base64Image:y.toString("base64")}}if(Ir.has(c)){let y=await at(i);return{path:n,content:"",binaryData:y}}let S=await at(i,"utf-8");return{path:n,content:S}}))}var jr=["name","api_name","version","entry"];function kt(e,t){let i=t===void 0?"kizen.json":`kizen.json entry #${String(t+1)}`;for(let n of jr){let r=e[n];if(typeof r!="string"||r==="")throw new Error(`${i} is missing required field "${n}" (expected a non-empty string).`)}}function Ar(e){let t;try{t=JSON.parse(e)}catch(i){let n=i instanceof Error?i.message:String(i);throw new Error(`kizen.json is not valid JSON: ${n}`)}if(Array.isArray(t))return t.forEach((i,n)=>{if(typeof i!="object"||i===null)throw new Error(`kizen.json entry #${String(n+1)} is not a JSON object.`);kt(i,n)}),t;if(typeof t!="object"||t===null)throw new Error("kizen.json must be a JSON object (or array of objects).");return kt(t),t}var Or=(e,t)=>({...e,thumbnail:e.thumbnail?Buffer.from(e.thumbnail).toString("base64"):null,kznFile:e.kznFile?Buffer.from(e.kznFile).toString("base64"):null,allReleaseNotes:t});async function ze(e,t,i){await Br(t,{recursive:!0}),i?.("reading-files");let n=await Et(e);i?.("minifying");let r=await Nr(n);i?.("packaging");let c=r.find(a=>a.path==="kizen.json");if(!c)throw new Error("kizen.json not found in plugin directory.");let S=Ar(c.content),y=Lr(r,S),_=Object.values(y).map(Fr);i?.("writing-bundle");let b=_.map(a=>{let v=a.release_notes_directory?.replace(/\/$/,""),N=v?n.filter(E=>E.path.startsWith(v+"/")&&E.path.endsWith(".md")&&E.content).map(E=>({version:E.path.slice(E.path.lastIndexOf("/")+1,-3),notes:E.content})).sort((E,C)=>C.version.localeCompare(E.version,void 0,{numeric:!0})):[];return Or(a,N)}),h=JSON.stringify(b,null,2),g=(a,v)=>a==="allReleaseNotes"?void 0:v,f=Buffer.byteLength(JSON.stringify(b,g,2),"utf-8");return await Dr(Rr(t,"bundle.json"),h,"utf-8"),{bundleSize:f}}function Se(e){return e<1024?`${String(e)} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(2)} MB`}import{Box as Hr,Text as qr}from"ink";import{Box as $r,Text as zr}from"ink";import{jsx as It}from"react/jsx-runtime";var Mr=[" :::::::: "," :::::: ::::::::::: "," :::::: :::::::::: "," :::::: :::::: ::::::: "," ::::: :::::: ::::::::::::::::: "," ::::: ::::: ::::::::::::::::::: "," ::::: ::::: :::::: ::::: ",":::: ::::: ::: ::::: :: ","::::: :::::: :::::::::: ","::::: :::::: ::::::::::: "," ::::: ::: ::::::: "," ::::::: ::: ::::: "," ::::::::::: :::::: :::::"," :::::::::: :::::: :::::"," :: ::::: ::: ::::: ::::"," ::::: :::::: ::::: ::::: ::: "," ::::::::::::::::::: ::::: ::::: "," ::::::::::::::::: ::::: ::::: "," ::::::::: :::::: :::::: "," :::::::::: :::::: "," ::::::::::: :::::: "," :::::::: "],Bt=()=>It($r,{flexDirection:"column",children:Mr.map((e,t)=>It(zr,{color:"cyan",children:e},t))});import{Fragment as Ur,jsx as lt,jsxs as Vr}from"react/jsx-runtime";var V=({marginBottom:e=0})=>Vr(Ur,{children:[lt(Bt,{}),lt(Hr,{flexDirection:"column",marginTop:1,marginBottom:e,children:lt(qr,{bold:!0,color:"cyan",children:"Kizen App Development Toolkit"})})]});import{useEffect as Jr,useState as Wr}from"react";import{Text as Kr}from"ink";import{jsx as Gr}from"react/jsx-runtime";var Dt=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Me=()=>{let[e,t]=Wr(0);return Jr(()=>{let i=setInterval(()=>{t(n=>(n+1)%Dt.length)},80);return()=>{clearInterval(i)}},[]),Gr(Kr,{color:"cyan",children:Dt[e]??"\u280B"})};import{Fragment as Zr,jsx as ae,jsxs as le}from"react/jsx-runtime";var Rt=["creating-dir","reading-files","minifying","packaging","writing-bundle"],He={"creating-dir":"Creating .kizenapp directory","reading-files":"Reading plugin files",minifying:"Minifying scripts",packaging:"Packaging plugin","writing-bundle":"Writing bundle.json"},Nt=({outputDir:e,pluginDir:t})=>{let i=Yr(),[n,r]=ct("creating-dir"),[c,S]=ct(null),[y,_]=ct(null);Xr(()=>{ze(t,e,r).then(({bundleSize:f})=>{_(f),r("done"),i.exit()}).catch(f=>{let a=f instanceof Error?f.message:String(f);S(a),r("error"),i.exit()})},[e,t,i]);let b=Rt.indexOf(n),h=n==="error",g=n==="done";return le(Pe,{flexDirection:"column",paddingY:1,paddingX:2,children:[ae(V,{marginBottom:1}),le(Pe,{flexDirection:"column",gap:0,children:[Rt.map((f,a)=>{let v=n===f,N=g||b>a;return ae(Pe,{gap:1,children:h&&a===b?le(me,{color:"red",children:["\u2717 ",He[f]]}):N?le(me,{color:"green",children:["\u2713 ",He[f]]}):v?le(Zr,{children:[ae(Me,{}),ae(me,{children:He[f]})]}):le(me,{dimColor:!0,children:[" ",He[f]]})},f)}),g&&y!==null&&ae(Pe,{marginTop:1,gap:1,children:le(me,{color:"green",children:["\u2713 bundle \u2014 ",Se(y)]})}),c!==null&&ae(Pe,{marginTop:1,borderStyle:"single",borderColor:"red",paddingX:1,children:ae(me,{color:"red",children:c})})]})]})};import*as ge from"fs";import*as Lt from"path";function ye(e){let t=Lt.join(e,".gitignore"),i=".kizenapp/",n="";if(ge.existsSync(t)){n=ge.readFileSync(t,"utf8");let c=n.split(`
|
|
3
|
+
`).map(S=>S.trim());if(c.includes(i)||c.includes(".kizenapp"))return}let r=n.endsWith(`
|
|
4
|
+
`)?i+`
|
|
5
|
+
`:`
|
|
6
|
+
`+i+`
|
|
7
|
+
`;ge.writeFileSync(t,n+r)}function Ft(e){e.command("build").description("Bundle the plugin app into .kizenapp directory").action(async()=>{let t=process.cwd(),i=`${t}/.kizenapp`;ye(t);let{waitUntilExit:n}=en(Qr(Nt,{outputDir:i,pluginDir:t}));await n()})}import{createElement as gn}from"react";import{render as yn}from"ink";import{useCallback as Ue,useState as Te}from"react";import{Box as M,Text as B,useApp as nn,useInput as on}from"ink";import{join as pt}from"path";import{access as tn,mkdir as dt,writeFile as rn}from"fs/promises";import{join as ce}from"path";async function qe(e){try{return await tn(e),!0}catch{return!1}}async function jt(e){return await qe(ce(e,"kizen.json"))?"has-manifest":await qe(ce(e,".kizenapp"))?"has-kizenapp":"ok"}async function At(e,t){let i=ce(e,t);if(!await qe(i))return i;for(let n=1;n<1e3;n++){let r=ce(e,`${t}-${String(n)}`);if(!await qe(r))return r}throw new Error(`Could not find an available directory name for "${t}"`)}function ut(e){return{name:e.name,version:"1.0.0",published:!0,api_name:e.apiName,external_link:e.externalLink,description:e.description,entry:"src/",engine:"1.0.0",release_notes_directory:"releaseNotes/",release_environments:["prod"],config_template:{},base_config:{},developer_business_id:e.developerBusinessId}}async function Ot(e){await dt(e.targetDir,{recursive:!0});let t=ut(e);await rn(ce(e.targetDir,"kizen.json"),JSON.stringify(t,null,2)+`
|
|
8
|
+
`,"utf-8"),await dt(ce(e.targetDir,"src"),{recursive:!0}),await dt(ce(e.targetDir,"releaseNotes"),{recursive:!0}),ye(e.targetDir)}import{jsx as T,jsxs as A}from"react/jsx-runtime";var de=["name","apiName","externalLink","description","developerBusinessId"],$t={name:"Name",apiName:"API name",externalLink:"External link",description:"Description",developerBusinessId:"Business ID"},zt=["name","apiName"];function sn(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")}var he=({text:e})=>T(B,{dimColor:!0,children:e});function an(e){return{name:"",apiName:"",externalLink:"",description:"",developerBusinessId:e}}var Mt=({parentDir:e,defaultBusinessId:t})=>{let i=nn(),[n,r]=Te({type:"target-select",cursor:0}),[c,S]=Te("in-place"),[y,_]=Te(()=>an(t)),[b,h]=Te(""),[g,f]=Te(e),a=Ue((C,m)=>{S(C),h(m.name),r({type:"field-entry",field:0,values:m})},[]),v=Ue(async C=>{let m=await jt(C);if(m!=="ok"){r({type:"precheck-error",targetDir:C,reason:m});return}f(C),a("in-place",y)},[y,a]),N=Ue(async C=>{try{let m=await At(e,C.apiName);f(m),r({type:"confirm",values:C})}catch(m){r({type:"error",message:m instanceof Error?m.message:String(m)})}},[e]),E=Ue(async(C,m)=>{try{await Ot({targetDir:m,name:C.name,apiName:C.apiName,externalLink:C.externalLink,description:C.description,developerBusinessId:C.developerBusinessId}),r({type:"done",targetDir:m})}catch(u){r({type:"error",message:u instanceof Error?u.message:String(u)})}},[]);if(on((C,m)=>{if(m.ctrl&&C==="c"){i.exit();return}if(n.type==="target-select"){m.upArrow?r({type:"target-select",cursor:0}):m.downArrow?r({type:"target-select",cursor:1}):m.return?n.cursor===0?v(e):a("sub-dir",y):m.escape&&i.exit();return}if(n.type==="precheck-error"){(m.return||m.escape)&&i.exit();return}if(n.type==="field-entry"){let{field:u,values:w}=n,s=de[u];if(!s)return;if(m.escape){i.exit();return}if(m.backspace||m.delete){h(o=>o.slice(0,-1));return}if(m.upArrow&&u>0){let o={...w,[s]:b},l=u-1,d=de[l];r({type:"field-entry",field:l,values:o}),_(o),h(d?o[d]:"");return}if(m.return||m.tab||m.downArrow){let o=b.trim();if(zt.includes(s)&&!o){r({type:"field-entry",field:u,values:w,fieldError:`${$t[s]} is required`});return}let d=s==="description"?b:o,p={...w,[s]:d};_(p);let F=u+1;if(F>=de.length)h(""),c==="sub-dir"?(r({type:"resolving-target"}),N(p)):r({type:"confirm",values:p});else{let k=de[F],I=k?p[k]:"",O=k==="apiName"&&!I?sn(p.name):I;r({type:"field-entry",field:F,values:p}),h(O)}return}C&&!m.ctrl&&!m.meta&&h(o=>o+C);return}if(n.type==="confirm"){if(m.escape){let u=de.length-1,w=de[u];r({type:"field-entry",field:u,values:n.values}),h(w?n.values[w]:"");return}m.return&&(r({type:"writing"}),E(n.values,g));return}(n.type==="done"||n.type==="error")&&(m.return||m.escape)&&i.exit()}),n.type==="target-select"){let C=[{label:"Use current directory",desc:e},{label:"Create new sub-directory",desc:`${e}/<inferred from API name>`}];return A(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[T(V,{}),A(M,{flexDirection:"column",gap:0,children:[T(B,{bold:!0,children:"Where should the new plugin live?"}),C.map((m,u)=>{let w=n.cursor===u;return A(M,{gap:2,children:[T(B,{...w&&{color:"cyan"},children:w?"\u276F":" "}),T(B,{bold:w,...w&&{color:"cyan"},children:m.label}),T(B,{dimColor:!0,children:m.desc})]},m.label)})]}),T(he,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel"})]})}if(n.type==="precheck-error"){let{targetDir:C,reason:m}=n;return A(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[T(V,{}),T(B,{color:"red",bold:!0,children:"Cannot create plugin"}),A(M,{flexDirection:"column",gap:0,children:[A(B,{children:[C," already contains ",T(B,{bold:!0,children:m==="has-manifest"?"kizen.json":".kizenapp/"}),"."]}),T(B,{dimColor:!0,children:"Remove the conflicting file/directory or choose a different target to avoid overwriting an existing plugin."})]}),T(he,{text:"Enter or Esc to exit"})]})}if(n.type==="field-entry"){let{field:C,fieldError:m}=n,u=c==="sub-dir"?`Target: sub-directory of ${e} (inferred from API name)`:`Target: ${g}`;return A(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[T(V,{}),A(M,{flexDirection:"column",gap:0,children:[T(B,{bold:!0,children:"Plugin details"}),T(B,{dimColor:!0,children:u})]}),m!==void 0&&A(B,{color:"red",children:["Error: ",m]}),T(M,{flexDirection:"column",gap:0,children:de.map((w,s)=>{let o=C===s,l=o?b:y[w],d=zt.includes(w),p=`${$t[w]}${d?"":" (optional)"}`;return A(M,{gap:2,children:[T(M,{width:24,children:T(B,{bold:o,...o&&{color:"cyan"},children:p})}),T(B,{...o&&{color:"cyan"},children:">"}),T(B,{children:l}),o&&T(B,{color:"cyan",children:"\u2588"})]},w)})}),T(he,{text:"Enter/Tab/\u2193 next \xB7 \u2191 previous \xB7 Backspace delete \xB7 Esc cancel"})]})}if(n.type==="confirm"){let C=ut({targetDir:g,name:n.values.name,apiName:n.values.apiName,externalLink:n.values.externalLink,description:n.values.description,developerBusinessId:n.values.developerBusinessId});return A(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[T(V,{}),A(M,{flexDirection:"column",gap:0,children:[T(B,{bold:!0,children:"Ready to create"}),A(B,{dimColor:!0,children:["Target: ",g]})]}),A(M,{flexDirection:"column",gap:0,children:[T(B,{bold:!0,children:"kizen.json preview"}),T(B,{children:JSON.stringify(C,null,2)})]}),A(M,{flexDirection:"column",gap:0,children:[T(B,{dimColor:!0,children:"Also creates:"}),A(B,{dimColor:!0,children:[" ",pt(g,"src/")]}),A(B,{dimColor:!0,children:[" ",pt(g,"releaseNotes/")]}),A(B,{dimColor:!0,children:[" ",pt(g,".gitignore")," (adds .kizenapp/)"]})]}),T(he,{text:"Enter to create \xB7 Esc to go back"})]})}return n.type==="resolving-target"?T(M,{paddingY:1,paddingX:2,children:T(B,{dimColor:!0,children:"Finding available sub-directory\u2026"})}):n.type==="writing"?T(M,{paddingY:1,paddingX:2,children:T(B,{dimColor:!0,children:"Creating plugin\u2026"})}):n.type==="done"?A(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[T(V,{}),A(B,{color:"green",bold:!0,children:["Plugin created at ",n.targetDir]}),A(M,{flexDirection:"column",gap:0,children:[T(B,{dimColor:!0,children:"Next steps:"}),A(B,{children:[" ","cd ",n.targetDir]}),A(B,{children:[" ","appbuilder dev"]})]}),T(he,{text:"Enter or Esc to exit"})]}):A(M,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[T(V,{}),T(B,{color:"red",bold:!0,children:"Failed to create plugin"}),T(B,{children:n.message}),T(he,{text:"Enter or Esc to exit"})]})};import{mkdir as Ht,readFile as ln,readdir as cn,writeFile as qt}from"fs/promises";import{homedir as dn}from"os";import{dirname as un,join as Ve}from"path";var Z=["go","fmo","staging","integration","test1"],_e=Ve(dn(),".kizenappbuilder"),xe=Ve(_e,"credentials.json"),re="credentials";function pn(e){return Z.includes(e)}function fn(e){if(typeof e!="object"||e===null)throw new Error("Credentials must be a JSON object");let t=e,i=t.environment;return{apiKey:typeof t.apiKey=="string"?t.apiKey:"",userId:typeof t.userId=="string"?t.userId:"",businessId:typeof t.businessId=="string"?t.businessId:"",environment:pn(i)?i:"go"}}async function Je(e){let t=await ln(e,"utf-8");return fn(JSON.parse(t))}async function Y(){try{return await Je(xe)}catch{return null}}async function mn(e){await Ht(un(xe),{recursive:!0,mode:448}),await qt(xe,JSON.stringify(e,null,2),{encoding:"utf-8",mode:384})}function Ut(e){return Ve(_e,`${e}.json`)}async function Ee(){let e=[{name:re,path:xe,isDefault:!0}];try{let t=await cn(_e);for(let i of t){if(!i.endsWith(".json"))continue;let n=i.slice(0,-5);n!==re&&e.push({name:n,path:Ve(_e,i),isDefault:!1})}}catch{}return e}async function Vt(e,t){if(e===re){await mn(t);return}await Ht(_e,{recursive:!0,mode:448}),await qt(Ut(e),JSON.stringify(t,null,2),{encoding:"utf-8",mode:384})}async function be(e){try{return await Je(Ut(e))}catch{return null}}function Jt(e){e.command("create").description("Scaffold a new Kizen plugin project").action(async()=>{let t=process.cwd(),n=(await Y())?.businessId??"",{waitUntilExit:r}=yn(gn(Mt,{parentDir:t,defaultBusinessId:n}),{exitOnCtrlC:!1});await r()})}import{createElement as gr}from"react";import{render as yr}from"ink";import{useCallback as ve,useEffect as et,useRef as Q,useState as W}from"react";import{Box as q,Text as R,useInput as co}from"ink";import{watch as uo}from"fs";import{createServer as po}from"http";import{join as pr}from"path";import{WebSocketServer as fo}from"ws";import{createReadStream as er,realpathSync as Rn}from"fs";import{access as Nn,readdir as Ln,readFile as Fn,realpath as jn,stat as tr}from"fs/promises";import{dirname as An,extname as rr,join as De,resolve as On,sep as bt}from"path";import{fileURLToPath as $n}from"url";var hn=new Set(["set-cookie","strict-transport-security","content-security-policy","content-security-policy-report-only","x-frame-options","content-encoding","content-length"]);function ft(e){let t={};for(let[i,n]of Object.entries(e)){let r=i.toLowerCase();hn.has(r)||r.startsWith("access-control-")||(t[i]=n)}return t}var mt=10*1024*1024,xn=6e4,bn=200,ke=class extends Error{bytesRead;limit;constructor(t,i){super(`Upstream response exceeded ${String(i)} bytes (read ${String(t)}).`),this.name="ProxyResponseTooLargeError",this.bytesRead=t,this.limit=i}};async function gt(e,t){let i=e.body;if(i===null)return Buffer.alloc(0);let n=[],r=0;for await(let c of i){if(r+=c.length,r>t)throw new ke(r,t);n.push(c)}return Buffer.concat(n)}function Wt(e={}){let t=new Map,{onChange:i}=e,n=()=>{i?.(t.size)},r=c=>{t.delete(c)&&n()};return{get(c,S){let y=t.get(c);if(y!==void 0){if(Date.now()-y.insertedAt<xn)return y.promise.then(b=>({response:b,fromCache:!0}));r(c)}if(t.size>=bn){let b=t.keys().next().value;b!==void 0&&r(b)}let _=S().then(async b=>{let h=ft(Object.fromEntries(b.headers)),g=await gt(b,mt);return{status:b.status,headers:h,body:g}}).catch(b=>{throw r(c),b});return t.set(c,{insertedAt:Date.now(),promise:_}),n(),_.then(b=>((b.status<200||b.status>=300)&&r(c),{response:b,fromCache:!1}))},clear(){t.clear(),n()}}}var yt={".html":"text/html; charset=utf-8",".js":"application/javascript; charset=utf-8",".mjs":"application/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".svg":"image/svg+xml",".ico":"image/x-icon",".json":"application/json; charset=utf-8",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".map":"application/json"};import{mkdir as vn,readFile as Cn,writeFile as wn}from"fs/promises";import{join as Kt}from"path";async function Ie(e){try{let t=await Cn(Kt(e,"config.json"),"utf-8");return JSON.parse(t)}catch{return{}}}async function ue(e,t){await vn(e,{recursive:!0}),await wn(Kt(e,"config.json"),JSON.stringify(t,null,2),"utf-8")}import{spawn as Yt}from"child_process";import{writeFile as Gt,rm as Zt,mkdtemp as Sn,access as Pn}from"fs/promises";import{join as Be}from"path";import{tmpdir as Tn}from"os";var _n=`
|
|
861
9
|
import sys, json, os, traceback
|
|
862
10
|
|
|
863
11
|
# --- Inputs proxy (attribute access) ---
|
|
@@ -1017,1824 +165,10 @@ sys.stdout.write('\\n__KIZEN_RESULT_START__\\n')
|
|
|
1017
165
|
sys.stdout.write(json.dumps(result, ensure_ascii=False))
|
|
1018
166
|
sys.stdout.write('\\n__KIZEN_RESULT_END__\\n')
|
|
1019
167
|
sys.stdout.flush()
|
|
1020
|
-
`.trim();
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
var VENV_PACKAGES = [
|
|
1024
|
-
|
|
1025
|
-
"bcrypt",
|
|
1026
|
-
"fpdf2",
|
|
1027
|
-
"lxml",
|
|
1028
|
-
"msgpack",
|
|
1029
|
-
"numpy",
|
|
1030
|
-
"paramiko",
|
|
1031
|
-
"pyjwt",
|
|
1032
|
-
"pypdf",
|
|
1033
|
-
"pytz",
|
|
1034
|
-
"requests",
|
|
1035
|
-
"tzdata"
|
|
1036
|
-
];
|
|
1037
|
-
var venvReady = null;
|
|
1038
|
-
function resolvePythonBinary(runtime) {
|
|
1039
|
-
const match = /^python-(\d+\.\d+)$/.exec(runtime);
|
|
1040
|
-
if (match?.[1]) {
|
|
1041
|
-
return `python${match[1]}`;
|
|
1042
|
-
}
|
|
1043
|
-
return "python3";
|
|
1044
|
-
}
|
|
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
|
-
}
|
|
1095
|
-
if (!venvReady) {
|
|
1096
|
-
venvReady = (async () => {
|
|
1097
|
-
const venvPython = join8(VENV_DIR, "bin", "python");
|
|
1098
|
-
try {
|
|
1099
|
-
await access2(venvPython);
|
|
1100
|
-
} catch {
|
|
1101
|
-
const systemPython = resolvePythonBinary(runtime);
|
|
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();
|
|
1120
|
-
}
|
|
1121
|
-
return venvPython;
|
|
1122
|
-
})();
|
|
1123
|
-
}
|
|
1124
|
-
return venvReady;
|
|
1125
|
-
}
|
|
1126
|
-
function parseResult(stdout) {
|
|
1127
|
-
const startMarker = "__KIZEN_RESULT_START__";
|
|
1128
|
-
const endMarker = "__KIZEN_RESULT_END__";
|
|
1129
|
-
const startIdx = stdout.indexOf(startMarker);
|
|
1130
|
-
const endIdx = stdout.indexOf(endMarker);
|
|
1131
|
-
if (startIdx === -1 || endIdx === -1) {
|
|
1132
|
-
return { userStdout: stdout, result: null };
|
|
1133
|
-
}
|
|
1134
|
-
const userStdout = stdout.slice(0, startIdx).trimEnd();
|
|
1135
|
-
const jsonStr = stdout.slice(startIdx + startMarker.length, endIdx).trim();
|
|
1136
|
-
try {
|
|
1137
|
-
const result = JSON.parse(jsonStr);
|
|
1138
|
-
return { userStdout, result };
|
|
1139
|
-
} catch {
|
|
1140
|
-
return { userStdout: stdout, result: null };
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
async function executePythonStep(params) {
|
|
1144
|
-
const timeout = params.timeout ?? DEFAULT_TIMEOUT;
|
|
1145
|
-
const pythonBin = await ensureVenv(params.scriptRuntime, params.onInstallProgress);
|
|
1146
|
-
const start = Date.now();
|
|
1147
|
-
let tmpDir;
|
|
1148
|
-
try {
|
|
1149
|
-
tmpDir = await mkdtemp(join8(tmpdir(), "kizen-step-"));
|
|
1150
|
-
const wrapperPath = join8(tmpDir, "wrapper.py");
|
|
1151
|
-
const scriptPath = join8(tmpDir, "script.py");
|
|
1152
|
-
await Promise.all([
|
|
1153
|
-
writeFile5(wrapperPath, WRAPPER_SCRIPT, "utf-8"),
|
|
1154
|
-
writeFile5(scriptPath, params.script, "utf-8")
|
|
1155
|
-
]);
|
|
1156
|
-
const env = {
|
|
1157
|
-
...process.env,
|
|
1158
|
-
__KIZEN_INPUTS__: JSON.stringify(params.inputs),
|
|
1159
|
-
__KIZEN_SECRETS__: JSON.stringify(params.secrets),
|
|
1160
|
-
__KIZEN_SCRIPT_PATH__: scriptPath
|
|
1161
|
-
};
|
|
1162
|
-
const { exitCode, stdout, stderr } = await new Promise((resolve2) => {
|
|
1163
|
-
const stdoutChunks = [];
|
|
1164
|
-
const stderrChunks = [];
|
|
1165
|
-
let settled = false;
|
|
1166
|
-
const proc = spawn(pythonBin, [wrapperPath], {
|
|
1167
|
-
env,
|
|
1168
|
-
cwd: tmpDir,
|
|
1169
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1170
|
-
});
|
|
1171
|
-
const timer = setTimeout(() => {
|
|
1172
|
-
proc.kill("SIGKILL");
|
|
1173
|
-
}, timeout);
|
|
1174
|
-
proc.stdout.on("data", (chunk) => {
|
|
1175
|
-
stdoutChunks.push(chunk);
|
|
1176
|
-
});
|
|
1177
|
-
proc.stderr.on("data", (chunk) => {
|
|
1178
|
-
stderrChunks.push(chunk);
|
|
1179
|
-
});
|
|
1180
|
-
const finish = (code) => {
|
|
1181
|
-
if (settled) {
|
|
1182
|
-
return;
|
|
1183
|
-
}
|
|
1184
|
-
settled = true;
|
|
1185
|
-
clearTimeout(timer);
|
|
1186
|
-
resolve2({
|
|
1187
|
-
exitCode: code ?? 1,
|
|
1188
|
-
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
1189
|
-
stderr: Buffer.concat(stderrChunks).toString("utf-8")
|
|
1190
|
-
});
|
|
1191
|
-
};
|
|
1192
|
-
proc.on("close", finish);
|
|
1193
|
-
proc.on("error", (err) => {
|
|
1194
|
-
if (settled) {
|
|
1195
|
-
return;
|
|
1196
|
-
}
|
|
1197
|
-
settled = true;
|
|
1198
|
-
clearTimeout(timer);
|
|
1199
|
-
resolve2({
|
|
1200
|
-
exitCode: 1,
|
|
1201
|
-
stdout: "",
|
|
1202
|
-
stderr: err.message
|
|
1203
|
-
});
|
|
1204
|
-
});
|
|
1205
|
-
});
|
|
1206
|
-
const durationMs = Date.now() - start;
|
|
1207
|
-
const { userStdout, result } = parseResult(stdout);
|
|
1208
|
-
if (result) {
|
|
1209
|
-
const base2 = {
|
|
1210
|
-
success: result.success,
|
|
1211
|
-
outputValues: result.outputValues,
|
|
1212
|
-
logs: result.logs,
|
|
1213
|
-
stdout: userStdout,
|
|
1214
|
-
stderr,
|
|
1215
|
-
exitCode,
|
|
1216
|
-
durationMs
|
|
1217
|
-
};
|
|
1218
|
-
if (result.error !== null) {
|
|
1219
|
-
base2.error = result.error;
|
|
1220
|
-
}
|
|
1221
|
-
if (result.httpRequests) {
|
|
1222
|
-
base2.httpRequests = result.httpRequests;
|
|
1223
|
-
}
|
|
1224
|
-
return base2;
|
|
1225
|
-
}
|
|
1226
|
-
const base = {
|
|
1227
|
-
success: false,
|
|
1228
|
-
outputValues: {},
|
|
1229
|
-
logs: [],
|
|
1230
|
-
stdout: userStdout,
|
|
1231
|
-
stderr,
|
|
1232
|
-
exitCode,
|
|
1233
|
-
durationMs
|
|
1234
|
-
};
|
|
1235
|
-
if (exitCode !== 0) {
|
|
1236
|
-
base.error = `Process exited with code ${String(exitCode)}`;
|
|
1237
|
-
}
|
|
1238
|
-
return base;
|
|
1239
|
-
} finally {
|
|
1240
|
-
if (tmpDir) {
|
|
1241
|
-
void rm(tmpDir, { recursive: true, force: true });
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
|
|
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
|
-
}
|
|
1295
|
-
function proxyLogEntryToString(entry) {
|
|
1296
|
-
if (entry.kind === "info") {
|
|
1297
|
-
return entry.message;
|
|
1298
|
-
}
|
|
1299
|
-
const cache = entry.fromCache ? " [cache]" : "";
|
|
1300
|
-
return `${entry.method} ${entry.url} \u2192 ${String(entry.status)}${cache}`;
|
|
1301
|
-
}
|
|
1302
|
-
function getViewerPath() {
|
|
1303
|
-
const filename = fileURLToPath(import.meta.url);
|
|
1304
|
-
return join9(dirname2(filename), "viewer");
|
|
1305
|
-
}
|
|
1306
|
-
async function fileExists(filePath) {
|
|
1307
|
-
try {
|
|
1308
|
-
await access3(filePath);
|
|
1309
|
-
return true;
|
|
1310
|
-
} catch {
|
|
1311
|
-
return false;
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast, onProxyCacheChange, lastPathRef, pluginDir) {
|
|
1315
|
-
const proxyCache = createProxyCache({ onChange: onProxyCacheChange });
|
|
1316
|
-
return (req, res) => {
|
|
1317
|
-
void (async () => {
|
|
1318
|
-
const url = req.url ?? "/";
|
|
1319
|
-
try {
|
|
1320
|
-
createServerLog(`Received request: ${url}`);
|
|
1321
|
-
if (url === "/api/credentials") {
|
|
1322
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1323
|
-
res.end(credentialsRef.current !== null ? JSON.stringify(credentialsRef.current) : "{}");
|
|
1324
|
-
return;
|
|
1325
|
-
}
|
|
1326
|
-
if (url === "/api/credential-profiles" && req.method === "GET") {
|
|
1327
|
-
const profiles = await listCredentialProfiles();
|
|
1328
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1329
|
-
res.end(JSON.stringify({ profiles, active: activeProfileRef.current }));
|
|
1330
|
-
return;
|
|
1331
|
-
}
|
|
1332
|
-
if (url === "/api/credentials/switch" && req.method === "POST") {
|
|
1333
|
-
const chunks = [];
|
|
1334
|
-
for await (const chunk of req) {
|
|
1335
|
-
chunks.push(chunk);
|
|
1336
|
-
}
|
|
1337
|
-
const body = JSON.parse(Buffer.concat(chunks).toString("utf-8") || "{}");
|
|
1338
|
-
const profileName = body.profile;
|
|
1339
|
-
const creds = profileName === void 0 || profileName === "" ? await loadGlobalCredentials() : await loadCredentialProfile(profileName);
|
|
1340
|
-
if (creds !== null) {
|
|
1341
|
-
credentialsRef.current = creds;
|
|
1342
|
-
activeProfileRef.current = profileName ?? void 0;
|
|
1343
|
-
const active = activeProfileRef.current;
|
|
1344
|
-
await saveConfig(outputDir, {
|
|
1345
|
-
credentialMode: "global",
|
|
1346
|
-
...active !== void 0 && { activeCredentialProfile: active }
|
|
1347
|
-
});
|
|
1348
|
-
broadcast({ type: "credentials-updated", credentials: creds });
|
|
1349
|
-
}
|
|
1350
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1351
|
-
res.end(JSON.stringify({ ok: creds !== null }));
|
|
1352
|
-
return;
|
|
1353
|
-
}
|
|
1354
|
-
if (url === "/api/bundle") {
|
|
1355
|
-
const bundlePath = join9(process.cwd(), ".kizenapp", "bundle.json");
|
|
1356
|
-
try {
|
|
1357
|
-
const content = await readFile4(bundlePath, "utf-8");
|
|
1358
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1359
|
-
res.end(content);
|
|
1360
|
-
} catch {
|
|
1361
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1362
|
-
res.end("{}");
|
|
1363
|
-
}
|
|
1364
|
-
return;
|
|
1365
|
-
}
|
|
1366
|
-
if (url === "/api/last-path" && req.method === "POST") {
|
|
1367
|
-
const chunks = [];
|
|
1368
|
-
for await (const chunk of req) {
|
|
1369
|
-
chunks.push(chunk);
|
|
1370
|
-
}
|
|
1371
|
-
const body = JSON.parse(Buffer.concat(chunks).toString("utf-8") || "{}");
|
|
1372
|
-
if (typeof body.path === "string") {
|
|
1373
|
-
lastPathRef.current = body.path;
|
|
1374
|
-
const currentConfig = await loadConfig(outputDir);
|
|
1375
|
-
await saveConfig(outputDir, { ...currentConfig, lastPath: body.path });
|
|
1376
|
-
}
|
|
1377
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1378
|
-
res.end('{"ok":true}');
|
|
1379
|
-
return;
|
|
1380
|
-
}
|
|
1381
|
-
if (url === "/api/execute-step" && req.method === "POST") {
|
|
1382
|
-
const chunks = [];
|
|
1383
|
-
for await (const chunk of req) {
|
|
1384
|
-
chunks.push(chunk);
|
|
1385
|
-
}
|
|
1386
|
-
const body = JSON.parse(Buffer.concat(chunks).toString("utf-8") || "{}");
|
|
1387
|
-
if (typeof body.script !== "string") {
|
|
1388
|
-
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
1389
|
-
res.end(JSON.stringify({ error: "Missing required field: script" }));
|
|
1390
|
-
return;
|
|
1391
|
-
}
|
|
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
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
};
|
|
1413
|
-
if (typeof body.timeout === "number") {
|
|
1414
|
-
stepParams.timeout = body.timeout;
|
|
1415
|
-
}
|
|
1416
|
-
const result = await executePythonStep(stepParams);
|
|
1417
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1418
|
-
res.end(JSON.stringify(result));
|
|
1419
|
-
return;
|
|
1420
|
-
}
|
|
1421
|
-
if (url === "/api/proxy-cache/clear") {
|
|
1422
|
-
proxyCache.clear();
|
|
1423
|
-
createProxyLog({ kind: "info", message: "Proxy cache cleared" });
|
|
1424
|
-
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1425
|
-
res.end('{"ok":true}');
|
|
1426
|
-
return;
|
|
1427
|
-
}
|
|
1428
|
-
if (url.startsWith("/api/proxy")) {
|
|
1429
|
-
const proxyTarget = req.headers["x-proxy-target"];
|
|
1430
|
-
if (typeof proxyTarget !== "string") {
|
|
1431
|
-
res.writeHead(400);
|
|
1432
|
-
res.end("Missing x-proxy-target header");
|
|
1433
|
-
return;
|
|
1434
|
-
}
|
|
1435
|
-
const upstreamPath = url.slice("/api/proxy".length) || "/";
|
|
1436
|
-
const upstreamUrl = `${proxyTarget}${upstreamPath}`;
|
|
1437
|
-
const method = req.method ?? "GET";
|
|
1438
|
-
const chunks = [];
|
|
1439
|
-
for await (const chunk of req) {
|
|
1440
|
-
chunks.push(chunk);
|
|
1441
|
-
}
|
|
1442
|
-
const { host, "x-proxy-target": _drop, ...forwardHeaders } = req.headers;
|
|
1443
|
-
if (method === "GET") {
|
|
1444
|
-
const cacheKey = `GET::${upstreamUrl}`;
|
|
1445
|
-
const { response: cached, fromCache } = await proxyCache.get(
|
|
1446
|
-
cacheKey,
|
|
1447
|
-
() => fetch(upstreamUrl, {
|
|
1448
|
-
method: "GET",
|
|
1449
|
-
headers: forwardHeaders
|
|
1450
|
-
})
|
|
1451
|
-
);
|
|
1452
|
-
createProxyLog({
|
|
1453
|
-
kind: "request",
|
|
1454
|
-
method: "GET",
|
|
1455
|
-
status: cached.status,
|
|
1456
|
-
fromCache,
|
|
1457
|
-
url: upstreamPath
|
|
1458
|
-
});
|
|
1459
|
-
res.writeHead(cached.status, cached.headers);
|
|
1460
|
-
res.end(cached.body);
|
|
1461
|
-
return;
|
|
1462
|
-
}
|
|
1463
|
-
const body = chunks.length > 0 ? Buffer.concat(chunks) : void 0;
|
|
1464
|
-
const resolvedBody = body && body.length > 0 ? body : void 0;
|
|
1465
|
-
const upstream = await fetch(upstreamUrl, {
|
|
1466
|
-
method,
|
|
1467
|
-
headers: forwardHeaders,
|
|
1468
|
-
...resolvedBody !== void 0 && { body: resolvedBody }
|
|
1469
|
-
});
|
|
1470
|
-
createProxyLog({
|
|
1471
|
-
kind: "request",
|
|
1472
|
-
method,
|
|
1473
|
-
status: upstream.status,
|
|
1474
|
-
fromCache: false,
|
|
1475
|
-
url: upstreamPath
|
|
1476
|
-
});
|
|
1477
|
-
const responseHeaders = Object.fromEntries(upstream.headers);
|
|
1478
|
-
delete responseHeaders["content-encoding"];
|
|
1479
|
-
delete responseHeaders["content-length"];
|
|
1480
|
-
res.writeHead(upstream.status, responseHeaders);
|
|
1481
|
-
res.end(Buffer.from(await upstream.arrayBuffer()));
|
|
1482
|
-
return;
|
|
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
|
-
}
|
|
1588
|
-
const rawPath = url === "/" ? "/index.html" : url;
|
|
1589
|
-
const filePath = join9(viewerPath, rawPath);
|
|
1590
|
-
const resolvedPath = await fileExists(filePath) ? filePath : join9(viewerPath, "index.html");
|
|
1591
|
-
const ext = extname(resolvedPath);
|
|
1592
|
-
const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
1593
|
-
res.writeHead(200, { "Content-Type": mimeType });
|
|
1594
|
-
createReadStream(resolvedPath).pipe(res);
|
|
1595
|
-
} catch (err) {
|
|
1596
|
-
if (url.startsWith("/api/proxy")) {
|
|
1597
|
-
createProxyLog({
|
|
1598
|
-
kind: "request",
|
|
1599
|
-
method: req.method ?? "GET",
|
|
1600
|
-
status: 502,
|
|
1601
|
-
fromCache: false,
|
|
1602
|
-
url: url.slice("/api/proxy".length) || "/"
|
|
1603
|
-
});
|
|
1604
|
-
} else {
|
|
1605
|
-
createProxyLog({
|
|
1606
|
-
kind: "info",
|
|
1607
|
-
message: `Error handling ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
1608
|
-
});
|
|
1609
|
-
}
|
|
1610
|
-
if (!res.headersSent) {
|
|
1611
|
-
res.writeHead(502);
|
|
1612
|
-
res.end("Bad Gateway");
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
})();
|
|
1616
|
-
};
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
// src/chrome/launcher.ts
|
|
1620
|
-
import { launch } from "chrome-launcher";
|
|
1621
|
-
import { createRequire } from "module";
|
|
1622
|
-
import { mkdirSync } from "fs";
|
|
1623
|
-
import { readFile as readFile5 } from "fs/promises";
|
|
1624
|
-
import { dirname as dirname3, join as join10 } from "path";
|
|
1625
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1626
|
-
var CDP = createRequire(import.meta.url)("chrome-remote-interface");
|
|
1627
|
-
var CSP_HEADERS = /* @__PURE__ */ new Set([
|
|
1628
|
-
"content-security-policy",
|
|
1629
|
-
"content-security-policy-report-only",
|
|
1630
|
-
"x-frame-options"
|
|
1631
|
-
]);
|
|
1632
|
-
var KIZEN_DOMAINS = ["kizen.dev", "kizen.com"];
|
|
1633
|
-
function isKizenUrl(url) {
|
|
1634
|
-
try {
|
|
1635
|
-
const { hostname } = new URL(url);
|
|
1636
|
-
return KIZEN_DOMAINS.some((d) => hostname === d || hostname.endsWith("." + d));
|
|
1637
|
-
} catch {
|
|
1638
|
-
return false;
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
function parseProp(prop) {
|
|
1642
|
-
if (prop.type === "number") {
|
|
1643
|
-
return Number(prop.value);
|
|
1644
|
-
}
|
|
1645
|
-
if (prop.type === "boolean") {
|
|
1646
|
-
return prop.value === "true";
|
|
1647
|
-
}
|
|
1648
|
-
if (prop.type === "undefined") {
|
|
1649
|
-
return void 0;
|
|
1650
|
-
}
|
|
1651
|
-
if (prop.type === "object" && prop.value === "null") {
|
|
1652
|
-
return null;
|
|
1653
|
-
}
|
|
1654
|
-
if (prop.type === "object" && prop.valuePreview) {
|
|
1655
|
-
const { valuePreview } = prop;
|
|
1656
|
-
if (valuePreview.subtype === "array") {
|
|
1657
|
-
return valuePreview.properties.filter((p) => /^\d+$/.test(p.name)).sort((a, b) => parseInt(a.name) - parseInt(b.name)).map(parseProp);
|
|
1658
|
-
}
|
|
1659
|
-
const obj = {};
|
|
1660
|
-
for (const p of valuePreview.properties) {
|
|
1661
|
-
obj[p.name] = parseProp(p);
|
|
1662
|
-
}
|
|
1663
|
-
return obj;
|
|
1664
|
-
}
|
|
1665
|
-
return prop.value ?? `[${prop.type}]`;
|
|
1666
|
-
}
|
|
1667
|
-
function serializeArg(arg) {
|
|
1668
|
-
if (arg.value !== void 0) {
|
|
1669
|
-
return arg.value;
|
|
1670
|
-
}
|
|
1671
|
-
if (arg.preview) {
|
|
1672
|
-
const { preview } = arg;
|
|
1673
|
-
if (preview.subtype === "array") {
|
|
1674
|
-
return preview.properties.filter((p) => /^\d+$/.test(p.name)).sort((a, b) => parseInt(a.name) - parseInt(b.name)).map(parseProp);
|
|
1675
|
-
}
|
|
1676
|
-
const obj = {};
|
|
1677
|
-
for (const prop of preview.properties) {
|
|
1678
|
-
obj[prop.name] = parseProp(prop);
|
|
1679
|
-
}
|
|
1680
|
-
return obj;
|
|
1681
|
-
}
|
|
1682
|
-
return arg.description ?? `[${arg.type}]`;
|
|
1683
|
-
}
|
|
1684
|
-
async function serializeArgAsync(client, arg) {
|
|
1685
|
-
if (arg.type === "object" && arg.objectId) {
|
|
1686
|
-
try {
|
|
1687
|
-
const result = await client.Runtime.callFunctionOn({
|
|
1688
|
-
objectId: arg.objectId,
|
|
1689
|
-
functionDeclaration: 'function() { const seen = new WeakSet(); return JSON.stringify(this, function(k,v) { if (typeof v === "object" && v !== null) { if (seen.has(v)) return "[Circular]"; seen.add(v); } return v; }); }',
|
|
1690
|
-
returnByValue: true
|
|
1691
|
-
});
|
|
1692
|
-
const val = result.result.value;
|
|
1693
|
-
if (typeof val === "string") {
|
|
1694
|
-
return JSON.parse(val);
|
|
1695
|
-
}
|
|
1696
|
-
} catch {
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
return serializeArg(arg);
|
|
1700
|
-
}
|
|
1701
|
-
async function installDockIcon(client) {
|
|
1702
|
-
try {
|
|
1703
|
-
const iconPath = join10(dirname3(fileURLToPath2(import.meta.url)), "viewer", "icon.png");
|
|
1704
|
-
const icon = await readFile5(iconPath);
|
|
1705
|
-
await client.Browser.setDockTile({ image: icon.toString("base64") });
|
|
1706
|
-
} catch {
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
async function launchChromeViewer(opts) {
|
|
1710
|
-
mkdirSync(opts.userDataDir, { recursive: true });
|
|
1711
|
-
const baseUrl = `http://localhost:${String(opts.port)}`;
|
|
1712
|
-
const chrome = await launch({
|
|
1713
|
-
startingUrl: baseUrl,
|
|
1714
|
-
chromeFlags: [
|
|
1715
|
-
`--app=${baseUrl}`,
|
|
1716
|
-
"--no-first-run",
|
|
1717
|
-
"--no-default-browser-check",
|
|
1718
|
-
"--disable-extensions"
|
|
1719
|
-
],
|
|
1720
|
-
userDataDir: opts.userDataDir,
|
|
1721
|
-
handleSIGINT: false,
|
|
1722
|
-
logLevel: "silent"
|
|
1723
|
-
});
|
|
1724
|
-
const cdpPort = chrome.port;
|
|
1725
|
-
let client = null;
|
|
1726
|
-
let cookieTimer = null;
|
|
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
|
-
};
|
|
1953
|
-
for (let attempt = 0; attempt < 10; attempt++) {
|
|
1954
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
1955
|
-
dbg(`CDP connect attempt ${String(attempt + 1)}/10`);
|
|
1956
|
-
try {
|
|
1957
|
-
const c = await CDP({ port: cdpPort });
|
|
1958
|
-
client = c;
|
|
1959
|
-
if (opts.path) {
|
|
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");
|
|
1974
|
-
}
|
|
1975
|
-
break;
|
|
1976
|
-
} catch {
|
|
1977
|
-
if (attempt === 9) {
|
|
1978
|
-
dbg("CDP unavailable after 10 attempts \u2014 viewer will run without interception");
|
|
1979
|
-
return {
|
|
1980
|
-
kill: () => {
|
|
1981
|
-
chrome.kill();
|
|
1982
|
-
}
|
|
1983
|
-
};
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
if (!client) {
|
|
1988
|
-
return {
|
|
1989
|
-
kill: () => {
|
|
1990
|
-
chrome.kill();
|
|
1991
|
-
}
|
|
1992
|
-
};
|
|
1993
|
-
}
|
|
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) {
|
|
2003
|
-
try {
|
|
2004
|
-
void client.close();
|
|
2005
|
-
} catch {
|
|
2006
|
-
}
|
|
2007
|
-
client = null;
|
|
2008
|
-
}
|
|
2009
|
-
chrome.kill();
|
|
2010
|
-
};
|
|
2011
|
-
return { kill };
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
|
-
// src/hooks/useLogHistory.ts
|
|
2015
|
-
import { useCallback as useCallback2, useState as useState4 } from "react";
|
|
2016
|
-
function useLogHistory(limit) {
|
|
2017
|
-
const [history, setHistory] = useState4([]);
|
|
2018
|
-
const append = useCallback2(
|
|
2019
|
-
(message) => {
|
|
2020
|
-
setHistory((h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-limit));
|
|
2021
|
-
},
|
|
2022
|
-
[limit]
|
|
2023
|
-
);
|
|
2024
|
-
return [history, append];
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
// src/ui/CredentialSetupUI.tsx
|
|
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 = {
|
|
2033
|
-
apiKey: "API Key",
|
|
2034
|
-
userId: "User ID",
|
|
2035
|
-
businessId: "Business ID"
|
|
2036
|
-
};
|
|
2037
|
-
var Hint2 = ({ text }) => /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: text });
|
|
2038
|
-
var CredentialSetupUI = ({
|
|
2039
|
-
initialMode,
|
|
2040
|
-
showProfileManager,
|
|
2041
|
-
onComplete,
|
|
2042
|
-
onCancel
|
|
2043
|
-
}) => {
|
|
2044
|
-
const [phase, setPhase] = useState5(
|
|
2045
|
-
initialMode === "global" && !showProfileManager ? { type: "loading" } : initialMode === "global" && showProfileManager ? { type: "profile-loading" } : { type: "mode-select", cursor: 0 }
|
|
2046
|
-
);
|
|
2047
|
-
const [inputBuffer, setInputBuffer] = useState5("");
|
|
2048
|
-
const [error, setError] = useState5(null);
|
|
2049
|
-
const [activeProfileName, setActiveProfileName] = useState5(void 0);
|
|
2050
|
-
const handleModeChosen = useCallback3(
|
|
2051
|
-
(mode) => {
|
|
2052
|
-
if (mode === "local") {
|
|
2053
|
-
onComplete({ mode: "local", credentials: null });
|
|
2054
|
-
return;
|
|
2055
|
-
}
|
|
2056
|
-
setPhase({ type: "profile-loading" });
|
|
2057
|
-
},
|
|
2058
|
-
[onComplete]
|
|
2059
|
-
);
|
|
2060
|
-
const handleProfileChosen = useCallback3(async (profile) => {
|
|
2061
|
-
if (profile === null) {
|
|
2062
|
-
setPhase({ type: "profile-loading" });
|
|
2063
|
-
const profiles = await listCredentialProfiles();
|
|
2064
|
-
setPhase({ type: "name-entry", nameBuffer: "", existingProfiles: profiles });
|
|
2065
|
-
return;
|
|
2066
|
-
}
|
|
2067
|
-
setActiveProfileName(profile.isDefault ? void 0 : profile.name);
|
|
2068
|
-
setPhase({ type: "loading" });
|
|
2069
|
-
const existing = profile.isDefault ? await loadGlobalCredentials() : await loadCredentialProfile(profile.name);
|
|
2070
|
-
const envCursor2 = existing ? Math.max(0, ENVIRONMENTS.indexOf(existing.environment)) : 0;
|
|
2071
|
-
setPhase({ type: "creds-entry", field: 0, values: existing ?? {}, envCursor: envCursor2 });
|
|
2072
|
-
}, []);
|
|
2073
|
-
const handleSave = useCallback3(
|
|
2074
|
-
async (values2, envCursor2) => {
|
|
2075
|
-
const environment = ENVIRONMENTS[envCursor2] ?? "go";
|
|
2076
|
-
const credentials = {
|
|
2077
|
-
apiKey: values2.apiKey ?? "",
|
|
2078
|
-
userId: values2.userId ?? "",
|
|
2079
|
-
businessId: values2.businessId ?? "",
|
|
2080
|
-
environment
|
|
2081
|
-
};
|
|
2082
|
-
setPhase({ type: "saving" });
|
|
2083
|
-
try {
|
|
2084
|
-
const profileName = activeProfileName ?? DEFAULT_PROFILE_NAME;
|
|
2085
|
-
await saveCredentialProfile(profileName, credentials);
|
|
2086
|
-
onComplete({
|
|
2087
|
-
mode: "global",
|
|
2088
|
-
credentials,
|
|
2089
|
-
...activeProfileName !== void 0 && { profileName: activeProfileName }
|
|
2090
|
-
});
|
|
2091
|
-
} catch (err) {
|
|
2092
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
2093
|
-
setPhase({ type: "creds-entry", field: 0, values: values2, envCursor: envCursor2 });
|
|
2094
|
-
}
|
|
2095
|
-
},
|
|
2096
|
-
[onComplete, activeProfileName]
|
|
2097
|
-
);
|
|
2098
|
-
useInput2((input, key) => {
|
|
2099
|
-
if (key.ctrl && input === "c") {
|
|
2100
|
-
process.exit(0);
|
|
2101
|
-
}
|
|
2102
|
-
if (key.escape) {
|
|
2103
|
-
if (phase.type === "creds-entry" || phase.type === "name-entry") {
|
|
2104
|
-
if (initialMode === "global" && showProfileManager) {
|
|
2105
|
-
setPhase({ type: "profile-loading" });
|
|
2106
|
-
void loadProfileList();
|
|
2107
|
-
return;
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
onCancel?.();
|
|
2111
|
-
return;
|
|
2112
|
-
}
|
|
2113
|
-
if (phase.type === "mode-select") {
|
|
2114
|
-
if (key.upArrow) {
|
|
2115
|
-
setPhase({ type: "mode-select", cursor: 0 });
|
|
2116
|
-
} else if (key.downArrow) {
|
|
2117
|
-
setPhase({ type: "mode-select", cursor: 1 });
|
|
2118
|
-
} else if (key.return) {
|
|
2119
|
-
const mode = phase.cursor === 0 ? "global" : "local";
|
|
2120
|
-
handleModeChosen(mode);
|
|
2121
|
-
}
|
|
2122
|
-
return;
|
|
2123
|
-
}
|
|
2124
|
-
if (phase.type === "profile-select") {
|
|
2125
|
-
const { profiles, cursor } = phase;
|
|
2126
|
-
const addNewIdx = profiles.length;
|
|
2127
|
-
const totalItems = profiles.length + 1;
|
|
2128
|
-
if (key.upArrow) {
|
|
2129
|
-
setPhase({ ...phase, cursor: (cursor - 1 + totalItems) % totalItems });
|
|
2130
|
-
} else if (key.downArrow) {
|
|
2131
|
-
setPhase({ ...phase, cursor: (cursor + 1) % totalItems });
|
|
2132
|
-
} else if (key.return) {
|
|
2133
|
-
if (cursor === addNewIdx) {
|
|
2134
|
-
setPhase({ type: "name-entry", nameBuffer: "", existingProfiles: profiles });
|
|
2135
|
-
} else {
|
|
2136
|
-
const selected = profiles[cursor] ?? profiles[0];
|
|
2137
|
-
if (selected) {
|
|
2138
|
-
void handleProfileChosen(selected);
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
return;
|
|
2143
|
-
}
|
|
2144
|
-
if (phase.type === "name-entry") {
|
|
2145
|
-
const { nameBuffer, existingProfiles } = phase;
|
|
2146
|
-
if (key.backspace || key.delete) {
|
|
2147
|
-
setPhase({ type: "name-entry", nameBuffer: nameBuffer.slice(0, -1), existingProfiles });
|
|
2148
|
-
} else if (key.return) {
|
|
2149
|
-
const trimmed = nameBuffer.trim();
|
|
2150
|
-
if (!trimmed) {
|
|
2151
|
-
setPhase({ ...phase, nameError: "Name cannot be empty" });
|
|
2152
|
-
return;
|
|
2153
|
-
}
|
|
2154
|
-
if (trimmed === DEFAULT_PROFILE_NAME) {
|
|
2155
|
-
setPhase({ ...phase, nameError: `"${DEFAULT_PROFILE_NAME}" is reserved` });
|
|
2156
|
-
return;
|
|
2157
|
-
}
|
|
2158
|
-
if (existingProfiles.some((p) => p.name === trimmed)) {
|
|
2159
|
-
setPhase({ ...phase, nameError: `Profile "${trimmed}" already exists` });
|
|
2160
|
-
return;
|
|
2161
|
-
}
|
|
2162
|
-
setActiveProfileName(trimmed);
|
|
2163
|
-
setPhase({ type: "creds-entry", field: 0, values: {}, envCursor: 0 });
|
|
2164
|
-
setInputBuffer("");
|
|
2165
|
-
} else if (input && !key.ctrl && !key.meta) {
|
|
2166
|
-
setPhase({ type: "name-entry", nameBuffer: nameBuffer + input, existingProfiles });
|
|
2167
|
-
}
|
|
2168
|
-
return;
|
|
2169
|
-
}
|
|
2170
|
-
if (phase.type === "creds-entry") {
|
|
2171
|
-
const { field, values: values2, envCursor: envCursor2 } = phase;
|
|
2172
|
-
const isEnvField2 = field === FIELDS2.length;
|
|
2173
|
-
if (isEnvField2) {
|
|
2174
|
-
if (key.leftArrow) {
|
|
2175
|
-
setPhase({
|
|
2176
|
-
...phase,
|
|
2177
|
-
envCursor: (envCursor2 - 1 + ENVIRONMENTS.length) % ENVIRONMENTS.length
|
|
2178
|
-
});
|
|
2179
|
-
} else if (key.rightArrow) {
|
|
2180
|
-
setPhase({ ...phase, envCursor: (envCursor2 + 1) % ENVIRONMENTS.length });
|
|
2181
|
-
} else if (key.upArrow) {
|
|
2182
|
-
setPhase({ ...phase, field: field - 1 });
|
|
2183
|
-
setInputBuffer(values2[FIELDS2[field - 1]] ?? "");
|
|
2184
|
-
} else if (key.return) {
|
|
2185
|
-
void handleSave(values2, envCursor2);
|
|
2186
|
-
}
|
|
2187
|
-
return;
|
|
2188
|
-
}
|
|
2189
|
-
const fieldName = FIELDS2[field];
|
|
2190
|
-
if (!fieldName) {
|
|
2191
|
-
return;
|
|
2192
|
-
}
|
|
2193
|
-
if (key.backspace || key.delete) {
|
|
2194
|
-
setInputBuffer((prev) => prev.slice(0, -1));
|
|
2195
|
-
} else if (key.upArrow && field > 0) {
|
|
2196
|
-
const updatedValues = { ...values2, [fieldName]: inputBuffer };
|
|
2197
|
-
const prevField = field - 1;
|
|
2198
|
-
setPhase({ ...phase, field: prevField, values: updatedValues });
|
|
2199
|
-
setInputBuffer(updatedValues[FIELDS2[prevField]] ?? "");
|
|
2200
|
-
} else if (key.return || key.tab || key.downArrow) {
|
|
2201
|
-
const updatedValues = { ...values2, [fieldName]: inputBuffer };
|
|
2202
|
-
const nextField = field + 1;
|
|
2203
|
-
setPhase({ ...phase, field: nextField, values: updatedValues });
|
|
2204
|
-
if (nextField < FIELDS2.length) {
|
|
2205
|
-
setInputBuffer(updatedValues[FIELDS2[nextField]] ?? "");
|
|
2206
|
-
} else {
|
|
2207
|
-
setInputBuffer("");
|
|
2208
|
-
}
|
|
2209
|
-
} else if (input && !key.ctrl && !key.meta) {
|
|
2210
|
-
setInputBuffer((prev) => prev + input);
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
});
|
|
2214
|
-
const loadProfileList = useCallback3(async () => {
|
|
2215
|
-
const profiles = await listCredentialProfiles();
|
|
2216
|
-
setPhase({ type: "profile-select", profiles, cursor: 0 });
|
|
2217
|
-
}, []);
|
|
2218
|
-
useEffect3(() => {
|
|
2219
|
-
if (phase.type === "profile-loading") {
|
|
2220
|
-
void loadProfileList();
|
|
2221
|
-
}
|
|
2222
|
-
}, [phase.type, loadProfileList]);
|
|
2223
|
-
useEffect3(() => {
|
|
2224
|
-
if (initialMode === "global" && !showProfileManager) {
|
|
2225
|
-
void (async () => {
|
|
2226
|
-
const existing = await loadGlobalCredentials();
|
|
2227
|
-
const envCursor2 = existing ? Math.max(0, ENVIRONMENTS.indexOf(existing.environment)) : 0;
|
|
2228
|
-
setPhase({ type: "creds-entry", field: 0, values: existing ?? {}, envCursor: envCursor2 });
|
|
2229
|
-
})();
|
|
2230
|
-
}
|
|
2231
|
-
}, []);
|
|
2232
|
-
useEffect3(() => {
|
|
2233
|
-
if (phase.type === "creds-entry" && phase.field === 0) {
|
|
2234
|
-
setInputBuffer(phase.values.apiKey ?? "");
|
|
2235
|
-
}
|
|
2236
|
-
}, [phase.type]);
|
|
2237
|
-
if (phase.type === "loading" || phase.type === "saving" || phase.type === "profile-loading") {
|
|
2238
|
-
const label = phase.type === "saving" ? "Saving credentials\u2026" : phase.type === "profile-loading" ? "Loading profiles\u2026" : "Loading credentials\u2026";
|
|
2239
|
-
return /* @__PURE__ */ jsx6(Box5, { paddingY: 1, paddingX: 2, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: label }) });
|
|
2240
|
-
}
|
|
2241
|
-
if (phase.type === "done") {
|
|
2242
|
-
return null;
|
|
2243
|
-
}
|
|
2244
|
-
if (phase.type === "mode-select") {
|
|
2245
|
-
const options = [
|
|
2246
|
-
{ label: "Global", desc: `~/.kizenappbuilder/credentials.json`, mode: "global" },
|
|
2247
|
-
{ label: "Local", desc: "Enter credentials in the browser dev tools", mode: "local" }
|
|
2248
|
-
];
|
|
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" }),
|
|
2253
|
-
options.map((opt, i) => {
|
|
2254
|
-
const selected = phase.cursor === i;
|
|
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 })
|
|
2259
|
-
] }, opt.mode);
|
|
2260
|
-
})
|
|
2261
|
-
] }),
|
|
2262
|
-
/* @__PURE__ */ jsx6(Hint2, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit" })
|
|
2263
|
-
] });
|
|
2264
|
-
}
|
|
2265
|
-
if (phase.type === "profile-select") {
|
|
2266
|
-
const { profiles, cursor } = phase;
|
|
2267
|
-
const addNewIdx = profiles.length;
|
|
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" }),
|
|
2272
|
-
profiles.map((profile, i) => {
|
|
2273
|
-
const selected = cursor === i;
|
|
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 })
|
|
2278
|
-
] }, profile.name);
|
|
2279
|
-
}),
|
|
2280
|
-
/* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
|
|
2281
|
-
/* @__PURE__ */ jsx6(Text6, { ...cursor === addNewIdx && { color: "cyan" }, children: cursor === addNewIdx ? "\u276F" : " " }),
|
|
2282
|
-
/* @__PURE__ */ jsx6(
|
|
2283
|
-
Text6,
|
|
2284
|
-
{
|
|
2285
|
-
bold: cursor === addNewIdx,
|
|
2286
|
-
...cursor === addNewIdx && { color: "cyan" },
|
|
2287
|
-
children: "+ Add new profile"
|
|
2288
|
-
}
|
|
2289
|
-
)
|
|
2290
|
-
] })
|
|
2291
|
-
] }),
|
|
2292
|
-
/* @__PURE__ */ jsx6(Hint2, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel" })
|
|
2293
|
-
] });
|
|
2294
|
-
}
|
|
2295
|
-
if (phase.type === "name-entry") {
|
|
2296
|
-
const { nameBuffer, nameError } = phase;
|
|
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: [
|
|
2301
|
-
"Error: ",
|
|
2302
|
-
nameError
|
|
2303
|
-
] }),
|
|
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" })
|
|
2309
|
-
] }),
|
|
2310
|
-
/* @__PURE__ */ jsx6(Hint2, { text: "Type a name \xB7 Enter to confirm \xB7 Esc to go back" })
|
|
2311
|
-
] });
|
|
2312
|
-
}
|
|
2313
|
-
const { field: activeField, values, envCursor } = phase;
|
|
2314
|
-
const isEnvField = activeField === FIELDS2.length;
|
|
2315
|
-
const profileLabel = activeProfileName ?? DEFAULT_PROFILE_NAME;
|
|
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: [
|
|
2321
|
-
"Saved to:",
|
|
2322
|
-
" ",
|
|
2323
|
-
activeProfileName !== void 0 ? `~/.kizenappbuilder/${activeProfileName}.json` : GLOBAL_CREDENTIALS_PATH
|
|
2324
|
-
] })
|
|
2325
|
-
] }),
|
|
2326
|
-
error && /* @__PURE__ */ jsxs4(Text6, { color: "red", children: [
|
|
2327
|
-
"Error: ",
|
|
2328
|
-
error
|
|
2329
|
-
] }),
|
|
2330
|
-
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 0, children: [
|
|
2331
|
-
FIELDS2.map((name, i) => {
|
|
2332
|
-
const isActive = activeField === i;
|
|
2333
|
-
const displayValue = isActive ? inputBuffer : values[name] ?? "";
|
|
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" })
|
|
2339
|
-
] }, name);
|
|
2340
|
-
}),
|
|
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) => {
|
|
2345
|
-
const isSelected = i === envCursor;
|
|
2346
|
-
if (isEnvField) {
|
|
2347
|
-
return /* @__PURE__ */ jsx6(Text6, { bold: isSelected, ...isSelected && { color: "cyan" }, children: isSelected ? `[${env}]` : env }, env);
|
|
2348
|
-
}
|
|
2349
|
-
return /* @__PURE__ */ jsx6(Text6, { dimColor: !isSelected, bold: isSelected, children: env }, env);
|
|
2350
|
-
}) })
|
|
2351
|
-
] })
|
|
2352
|
-
] }),
|
|
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: [
|
|
2355
|
-
"Profile: ",
|
|
2356
|
-
profileLabel
|
|
2357
|
-
] })
|
|
2358
|
-
] });
|
|
2359
|
-
};
|
|
2360
|
-
|
|
2361
|
-
// src/ui/DevUI.tsx
|
|
2362
|
-
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2363
|
-
var SKIP_WATCH_PREFIXES = [".kizenapp", ".git"];
|
|
2364
|
-
var DevUI = ({
|
|
2365
|
-
port,
|
|
2366
|
-
pluginDir,
|
|
2367
|
-
outputDir,
|
|
2368
|
-
credentials: initialCredentials,
|
|
2369
|
-
credentialMode: initialCredentialMode,
|
|
2370
|
-
activeCredentialProfile: initialActiveProfile,
|
|
2371
|
-
lastPath: initialLastPath,
|
|
2372
|
-
debug = false,
|
|
2373
|
-
verbose = false
|
|
2374
|
-
}) => {
|
|
2375
|
-
const credentialsRef = useRef(initialCredentials);
|
|
2376
|
-
const activeProfileRef = useRef(initialActiveProfile);
|
|
2377
|
-
const lastPathRef = useRef(initialLastPath);
|
|
2378
|
-
const [credMode, setCredMode] = useState6("main");
|
|
2379
|
-
const [credentialMode, setCredentialMode] = useState6(
|
|
2380
|
-
initialCredentialMode
|
|
2381
|
-
);
|
|
2382
|
-
const [status, setStatus] = useState6("starting");
|
|
2383
|
-
const [errorMessage, setErrorMessage] = useState6(null);
|
|
2384
|
-
const [serverLogHistory, appendServerLog] = useLogHistory(LOG_LIMIT);
|
|
2385
|
-
const [buildLogHistory, appendBuildLog] = useLogHistory(LOG_LIMIT);
|
|
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);
|
|
2396
|
-
const buildingRef = useRef(false);
|
|
2397
|
-
const viewerLaunchedRef = useRef(false);
|
|
2398
|
-
const chromeHandleRef = useRef(null);
|
|
2399
|
-
const debounceTimerRef = useRef(null);
|
|
2400
|
-
const wsClientsRef = useRef(/* @__PURE__ */ new Set());
|
|
2401
|
-
const pendingMessagesRef = useRef([]);
|
|
2402
|
-
const broadcast = useCallback4((msg) => {
|
|
2403
|
-
const json = JSON.stringify(msg);
|
|
2404
|
-
if (wsClientsRef.current.size === 0) {
|
|
2405
|
-
pendingMessagesRef.current = [...pendingMessagesRef.current, json].slice(-LOG_LIMIT);
|
|
2406
|
-
return;
|
|
2407
|
-
}
|
|
2408
|
-
for (const client of wsClientsRef.current) {
|
|
2409
|
-
if (client.readyState === 1) {
|
|
2410
|
-
client.send(json);
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
}, []);
|
|
2414
|
-
const createServerLog = appendServerLog;
|
|
2415
|
-
const launchChromeViewerCb = useCallback4(() => {
|
|
2416
|
-
chromeHandleRef.current?.kill();
|
|
2417
|
-
chromeHandleRef.current = null;
|
|
2418
|
-
void launchChromeViewer({
|
|
2419
|
-
port,
|
|
2420
|
-
userDataDir: join11(outputDir, ".chrome"),
|
|
2421
|
-
broadcast,
|
|
2422
|
-
...lastPathRef.current !== void 0 && { path: lastPathRef.current },
|
|
2423
|
-
...debug && { debugLog: appendCdpLog },
|
|
2424
|
-
...verbose && { verbose: true }
|
|
2425
|
-
}).then((handle) => {
|
|
2426
|
-
chromeHandleRef.current = handle;
|
|
2427
|
-
});
|
|
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(
|
|
2439
|
-
(input, key) => {
|
|
2440
|
-
if (input === "q" || key.ctrl && input === "c") {
|
|
2441
|
-
process.exit(0);
|
|
2442
|
-
}
|
|
2443
|
-
if (input === "c") {
|
|
2444
|
-
setCredMode("editing");
|
|
2445
|
-
}
|
|
2446
|
-
if (input === "v") {
|
|
2447
|
-
launchChromeViewerCb();
|
|
2448
|
-
}
|
|
2449
|
-
},
|
|
2450
|
-
{ isActive: credMode === "main" }
|
|
2451
|
-
);
|
|
2452
|
-
useEffect4(() => {
|
|
2453
|
-
if (status !== "running" || viewerLaunchedRef.current) {
|
|
2454
|
-
return;
|
|
2455
|
-
}
|
|
2456
|
-
viewerLaunchedRef.current = true;
|
|
2457
|
-
launchChromeViewerCb();
|
|
2458
|
-
}, [status, launchChromeViewerCb]);
|
|
2459
|
-
const handleCredentialsDone = useCallback4(
|
|
2460
|
-
(result) => {
|
|
2461
|
-
credentialsRef.current = result.credentials;
|
|
2462
|
-
activeProfileRef.current = result.profileName;
|
|
2463
|
-
setCredMode("main");
|
|
2464
|
-
setCredentialMode(result.mode);
|
|
2465
|
-
const profile = result.profileName;
|
|
2466
|
-
void saveConfig(outputDir, {
|
|
2467
|
-
credentialMode: result.mode,
|
|
2468
|
-
...profile !== void 0 && { activeCredentialProfile: profile }
|
|
2469
|
-
});
|
|
2470
|
-
broadcast({ type: "credentials-updated", credentials: result.credentials });
|
|
2471
|
-
},
|
|
2472
|
-
[outputDir, broadcast]
|
|
2473
|
-
);
|
|
2474
|
-
const createProxyLog = useCallback4(
|
|
2475
|
-
(entry) => {
|
|
2476
|
-
setProxyLogHistory(
|
|
2477
|
-
(h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${proxyLogEntryToString(entry)}`].slice(
|
|
2478
|
-
-LOG_LIMIT
|
|
2479
|
-
)
|
|
2480
|
-
);
|
|
2481
|
-
if (entry.kind === "request") {
|
|
2482
|
-
if (entry.fromCache) {
|
|
2483
|
-
setProxyCacheHits((n) => n + 1);
|
|
2484
|
-
} else {
|
|
2485
|
-
setProxyCacheMisses((n) => n + 1);
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
broadcast({ type: "proxy-log", entry });
|
|
2489
|
-
},
|
|
2490
|
-
[broadcast]
|
|
2491
|
-
);
|
|
2492
|
-
const createBuildLog = useCallback4(
|
|
2493
|
-
(message) => {
|
|
2494
|
-
appendBuildLog(message);
|
|
2495
|
-
broadcast({ type: "log", message });
|
|
2496
|
-
},
|
|
2497
|
-
[appendBuildLog, broadcast]
|
|
2498
|
-
);
|
|
2499
|
-
const triggerBuild = useCallback4(() => {
|
|
2500
|
-
if (buildingRef.current) {
|
|
2501
|
-
return;
|
|
2502
|
-
}
|
|
2503
|
-
buildingRef.current = true;
|
|
2504
|
-
setBuildStatus("building");
|
|
2505
|
-
setBuildError(null);
|
|
2506
|
-
createBuildLog("Build started");
|
|
2507
|
-
void runBuild(pluginDir, outputDir).then(({ bundleSize }) => {
|
|
2508
|
-
createBuildLog(`Build finished \u2014 bundle: ${formatBytes(bundleSize)}`);
|
|
2509
|
-
setBuildStatus("done");
|
|
2510
|
-
setLastBuilt(/* @__PURE__ */ new Date());
|
|
2511
|
-
setLastBundleSize(bundleSize);
|
|
2512
|
-
createBuildLog("Notifying viewers to reload");
|
|
2513
|
-
for (const client of wsClientsRef.current) {
|
|
2514
|
-
if (client.readyState === 1) {
|
|
2515
|
-
client.send(JSON.stringify({ type: "rebuild" }));
|
|
2516
|
-
}
|
|
2517
|
-
}
|
|
2518
|
-
}).catch((err) => {
|
|
2519
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2520
|
-
setBuildError(message);
|
|
2521
|
-
setBuildStatus("error");
|
|
2522
|
-
}).finally(() => {
|
|
2523
|
-
buildingRef.current = false;
|
|
2524
|
-
});
|
|
2525
|
-
}, [pluginDir, outputDir, createBuildLog]);
|
|
2526
|
-
useEffect4(() => {
|
|
2527
|
-
triggerBuild();
|
|
2528
|
-
const watcher = watch(pluginDir, { recursive: true }, (_, filename) => {
|
|
2529
|
-
if (!filename) {
|
|
2530
|
-
return;
|
|
2531
|
-
}
|
|
2532
|
-
const normalized = filename.replace(/\\/g, "/");
|
|
2533
|
-
if (SKIP_WATCH_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
2534
|
-
return;
|
|
2535
|
-
}
|
|
2536
|
-
createBuildLog(`File change detected: ${filename}`);
|
|
2537
|
-
if (debounceTimerRef.current !== null) {
|
|
2538
|
-
clearTimeout(debounceTimerRef.current);
|
|
2539
|
-
}
|
|
2540
|
-
debounceTimerRef.current = setTimeout(triggerBuild, FILE_WATCH_DEBOUNCE_MS);
|
|
2541
|
-
});
|
|
2542
|
-
return () => {
|
|
2543
|
-
watcher.close();
|
|
2544
|
-
if (debounceTimerRef.current !== null) {
|
|
2545
|
-
clearTimeout(debounceTimerRef.current);
|
|
2546
|
-
}
|
|
2547
|
-
};
|
|
2548
|
-
}, [pluginDir, triggerBuild, createBuildLog]);
|
|
2549
|
-
useEffect4(() => {
|
|
2550
|
-
const viewerPath = getViewerPath();
|
|
2551
|
-
void fileExists(join11(viewerPath, "index.html")).then((viewerBuilt) => {
|
|
2552
|
-
if (!viewerBuilt) {
|
|
2553
|
-
setErrorMessage("Viewer not built. Run 'pnpm build:viewer' first.");
|
|
2554
|
-
setStatus("error");
|
|
2555
|
-
return;
|
|
2556
|
-
}
|
|
2557
|
-
const handler = createRequestHandler(
|
|
2558
|
-
viewerPath,
|
|
2559
|
-
createServerLog,
|
|
2560
|
-
createProxyLog,
|
|
2561
|
-
credentialsRef,
|
|
2562
|
-
activeProfileRef,
|
|
2563
|
-
outputDir,
|
|
2564
|
-
broadcast,
|
|
2565
|
-
setProxyCacheSize,
|
|
2566
|
-
lastPathRef,
|
|
2567
|
-
pluginDir
|
|
2568
|
-
);
|
|
2569
|
-
const server = createServer(handler);
|
|
2570
|
-
const wss = new WebSocketServer({ server });
|
|
2571
|
-
wss.on("connection", (ws) => {
|
|
2572
|
-
for (const json of pendingMessagesRef.current) {
|
|
2573
|
-
if (ws.readyState === 1) {
|
|
2574
|
-
ws.send(json);
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2577
|
-
pendingMessagesRef.current = [];
|
|
2578
|
-
createServerLog("Viewer connected for live reload");
|
|
2579
|
-
wsClientsRef.current.add(ws);
|
|
2580
|
-
setWsClientCount(wsClientsRef.current.size);
|
|
2581
|
-
ws.on("close", () => {
|
|
2582
|
-
wsClientsRef.current.delete(ws);
|
|
2583
|
-
setWsClientCount(wsClientsRef.current.size);
|
|
2584
|
-
});
|
|
2585
|
-
});
|
|
2586
|
-
server.listen(port, () => {
|
|
2587
|
-
setStatus("running");
|
|
2588
|
-
createServerLog(`Server started on port ${String(port)}`);
|
|
2589
|
-
});
|
|
2590
|
-
server.on("error", (err) => {
|
|
2591
|
-
setErrorMessage(err.message);
|
|
2592
|
-
setStatus("error");
|
|
2593
|
-
});
|
|
2594
|
-
return () => {
|
|
2595
|
-
wss.close();
|
|
2596
|
-
server.close();
|
|
2597
|
-
};
|
|
2598
|
-
});
|
|
2599
|
-
}, [port, createServerLog, createProxyLog, broadcast, outputDir, pluginDir]);
|
|
2600
|
-
if (credMode === "editing") {
|
|
2601
|
-
return /* @__PURE__ */ jsx7(
|
|
2602
|
-
CredentialSetupUI,
|
|
2603
|
-
{
|
|
2604
|
-
...credentialMode !== void 0 && { initialMode: credentialMode },
|
|
2605
|
-
showProfileManager: credentialMode === "global",
|
|
2606
|
-
onComplete: handleCredentialsDone,
|
|
2607
|
-
onCancel: () => {
|
|
2608
|
-
setCredMode("main");
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
);
|
|
2612
|
-
}
|
|
2613
|
-
const elapsedSeconds = lastBuilt !== null ? Math.round((Date.now() - lastBuilt.getTime()) / 1e3) : null;
|
|
2614
|
-
const elapsedLabel = elapsedSeconds === null ? "" : elapsedSeconds < 5 ? " (just now)" : ` (${String(elapsedSeconds)}s ago)`;
|
|
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" })
|
|
2624
|
-
] }),
|
|
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: [
|
|
2628
|
-
"at ",
|
|
2629
|
-
lastBuilt.toLocaleTimeString(),
|
|
2630
|
-
elapsedLabel
|
|
2631
|
-
] }),
|
|
2632
|
-
lastBundleSize !== null && /* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
2633
|
-
"\xB7 bundle: ",
|
|
2634
|
-
formatBytes(lastBundleSize)
|
|
2635
|
-
] })
|
|
2636
|
-
] }),
|
|
2637
|
-
buildStatus === "error" && /* @__PURE__ */ jsxs5(Text7, { color: "red", children: [
|
|
2638
|
-
"\u2717 ",
|
|
2639
|
-
buildError ?? "unknown error"
|
|
2640
|
-
] })
|
|
2641
|
-
] }),
|
|
2642
|
-
/* @__PURE__ */ jsx7(
|
|
2643
|
-
Box6,
|
|
2644
|
-
{
|
|
2645
|
-
flexDirection: "column",
|
|
2646
|
-
height: LOG_DISPLAY + 2,
|
|
2647
|
-
borderStyle: "single",
|
|
2648
|
-
borderColor: "gray",
|
|
2649
|
-
overflow: "hidden",
|
|
2650
|
-
children: buildLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate", children: log }, index))
|
|
2651
|
-
}
|
|
2652
|
-
)
|
|
2653
|
-
] }),
|
|
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: [
|
|
2661
|
-
"\u25CF ",
|
|
2662
|
-
wsClientCount,
|
|
2663
|
-
" viewer",
|
|
2664
|
-
wsClientCount !== 1 ? "s" : ""
|
|
2665
|
-
] })
|
|
2666
|
-
] }),
|
|
2667
|
-
status === "error" && /* @__PURE__ */ jsxs5(Text7, { color: "red", children: [
|
|
2668
|
-
"\u2717 ",
|
|
2669
|
-
errorMessage ?? "Server error"
|
|
2670
|
-
] })
|
|
2671
|
-
] }),
|
|
2672
|
-
/* @__PURE__ */ jsx7(
|
|
2673
|
-
Box6,
|
|
2674
|
-
{
|
|
2675
|
-
flexDirection: "column",
|
|
2676
|
-
height: LOG_DISPLAY + 2,
|
|
2677
|
-
borderStyle: "single",
|
|
2678
|
-
borderColor: "gray",
|
|
2679
|
-
overflow: "hidden",
|
|
2680
|
-
children: serverLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, wrap: "truncate", children: log }, index))
|
|
2681
|
-
}
|
|
2682
|
-
)
|
|
2683
|
-
] }),
|
|
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: [
|
|
2689
|
-
"\u25CF ",
|
|
2690
|
-
proxyCacheSize,
|
|
2691
|
-
" key",
|
|
2692
|
-
proxyCacheSize !== 1 ? "s" : "",
|
|
2693
|
-
" in the network cache \xB7",
|
|
2694
|
-
" ",
|
|
2695
|
-
proxyCacheHits,
|
|
2696
|
-
" hit \xB7 ",
|
|
2697
|
-
proxyCacheMisses,
|
|
2698
|
-
" miss"
|
|
2699
|
-
] })
|
|
2700
|
-
] }),
|
|
2701
|
-
/* @__PURE__ */ jsx7(
|
|
2702
|
-
Box6,
|
|
2703
|
-
{
|
|
2704
|
-
flexDirection: "column",
|
|
2705
|
-
height: LOG_DISPLAY + 2,
|
|
2706
|
-
borderStyle: "single",
|
|
2707
|
-
borderColor: "gray",
|
|
2708
|
-
overflow: "hidden",
|
|
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))
|
|
2710
|
-
}
|
|
2711
|
-
)
|
|
2712
|
-
] }),
|
|
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: [
|
|
2742
|
-
"] ",
|
|
2743
|
-
wsClientCount === 0 ? "open viewer" : "relaunch viewer"
|
|
2744
|
-
] })
|
|
2745
|
-
] })
|
|
2746
|
-
] });
|
|
2747
|
-
};
|
|
2748
|
-
|
|
2749
|
-
// src/commands/dev.ts
|
|
2750
|
-
async function runSetupUI(initialMode) {
|
|
2751
|
-
return new Promise((resolve2) => {
|
|
2752
|
-
let unmountFn = null;
|
|
2753
|
-
const handleComplete = (result) => {
|
|
2754
|
-
unmountFn?.();
|
|
2755
|
-
resolve2(result);
|
|
2756
|
-
};
|
|
2757
|
-
const { unmount } = render3(
|
|
2758
|
-
createElement3(CredentialSetupUI, {
|
|
2759
|
-
...initialMode !== void 0 && { initialMode },
|
|
2760
|
-
onComplete: handleComplete
|
|
2761
|
-
}),
|
|
2762
|
-
{ exitOnCtrlC: false }
|
|
2763
|
-
);
|
|
2764
|
-
unmountFn = unmount;
|
|
2765
|
-
});
|
|
2766
|
-
}
|
|
2767
|
-
function devCommand(program2) {
|
|
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 {
|
|
2794
|
-
credentials = await loadGlobalCredentials();
|
|
2795
|
-
}
|
|
2796
|
-
if (!credentials) {
|
|
2797
|
-
const result = await runSetupUI("global");
|
|
2798
|
-
credentials = result.credentials;
|
|
2799
|
-
activeCredentialProfile = result.profileName;
|
|
2800
|
-
}
|
|
2801
|
-
} else {
|
|
2802
|
-
const result = await runSetupUI();
|
|
2803
|
-
credentials = result.credentials;
|
|
2804
|
-
credentialMode = result.mode;
|
|
2805
|
-
activeCredentialProfile = result.profileName;
|
|
2806
|
-
const profile = result.profileName;
|
|
2807
|
-
await saveConfig(outputDir, {
|
|
2808
|
-
credentialMode: result.mode,
|
|
2809
|
-
...profile !== void 0 && { activeCredentialProfile: profile }
|
|
2810
|
-
});
|
|
2811
|
-
}
|
|
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();
|
|
2830
|
-
}
|
|
2831
|
-
);
|
|
2832
|
-
}
|
|
2833
|
-
|
|
2834
|
-
// src/index.ts
|
|
2835
|
-
program.name("appbuilder").description("Kizen plugin app builder").version("0.1.0");
|
|
2836
|
-
createCommand(program);
|
|
2837
|
-
buildCommand(program);
|
|
2838
|
-
devCommand(program);
|
|
2839
|
-
program.parse();
|
|
168
|
+
`.trim(),En=3e4,ht=Be(process.cwd(),".kizenapp","venv"),kn=["awslambdaric","bcrypt","fpdf2","lxml","msgpack","numpy","paramiko","pyjwt","pypdf","pytz","requests","tzdata"],We=null;function In(e){let t=/^python-(\d+\.\d+)$/.exec(e);return t?.[1]?`python${t[1]}`:"python3"}function Xt(e,t,i){return new Promise((n,r)=>{let c=Yt(e,t,{stdio:["ignore","pipe","pipe"]}),S=(_,b)=>{let h=[],g="";return _.on("data",f=>{g+=f.toString("utf-8");let a=g.indexOf(`
|
|
169
|
+
`);for(;a>=0;){let v=g.slice(0,a).replace(/\r$/,"");g=g.slice(a+1),h.push(v),i?.(v,b),a=g.indexOf(`
|
|
170
|
+
`)}}),_.on("end",()=>{if(g.length>0){let f=g.replace(/\r$/,"");h.push(f),i?.(f,b)}}),h},y=S(c.stderr,"stderr");S(c.stdout,"stdout"),c.on("error",r),c.on("close",_=>{if(_===0)n();else{let b=y.slice(-10).join(`
|
|
171
|
+
`).trim(),h=b.length>0?`: ${b}`:"";r(new Error(`${e} exited with code ${String(_)}${h}`))}})})}var xt=new Set;function Ke(e){for(let t of xt)t(e)}function Bn(e,t){return t&&xt.add(t),We||(We=(async()=>{let i=Be(ht,"bin","python");try{await Pn(i)}catch{let n=In(e);await Xt(n,["-m","venv",ht])}Ke({kind:"start"});try{await Xt(i,["-m","pip","install","--disable-pip-version-check",...kn],(n,r)=>{Ke({kind:"log",line:n,stream:r})}),Ke({kind:"complete"})}catch(n){let r=n instanceof Error?n.message:String(n);throw Ke({kind:"error",message:r}),We=null,await Zt(ht,{recursive:!0,force:!0}).catch(()=>{}),n}finally{xt.clear()}return i})()),We}function Dn(e){let t="__KIZEN_RESULT_START__",i="__KIZEN_RESULT_END__",n=e.indexOf(t),r=e.indexOf(i);if(n===-1||r===-1)return{userStdout:e,result:null};let c=e.slice(0,n).trimEnd(),S=e.slice(n+t.length,r).trim();try{let y=JSON.parse(S);return{userStdout:c,result:y}}catch{return{userStdout:e,result:null}}}async function Qt(e){let t=e.timeout??En,i=await Bn(e.scriptRuntime,e.onInstallProgress),n=Date.now(),r;try{r=await Sn(Be(Tn(),"kizen-step-"));let c=Be(r,"wrapper.py"),S=Be(r,"script.py");await Promise.all([Gt(c,_n,"utf-8"),Gt(S,e.script,"utf-8")]);let y={...process.env,__KIZEN_INPUTS__:JSON.stringify(e.inputs),__KIZEN_SECRETS__:JSON.stringify(e.secrets),__KIZEN_SCRIPT_PATH__:S},{exitCode:_,stdout:b,stderr:h}=await new Promise(N=>{let E=[],C=[],m=!1,u=Yt(i,[c],{env:y,cwd:r,stdio:["ignore","pipe","pipe"]}),w=setTimeout(()=>{u.kill("SIGKILL")},t);u.stdout.on("data",o=>{E.push(o)}),u.stderr.on("data",o=>{C.push(o)});let s=o=>{m||(m=!0,clearTimeout(w),N({exitCode:o??1,stdout:Buffer.concat(E).toString("utf-8"),stderr:Buffer.concat(C).toString("utf-8")}))};u.on("close",s),u.on("error",o=>{m||(m=!0,clearTimeout(w),N({exitCode:1,stdout:"",stderr:o.message}))})}),g=Date.now()-n,{userStdout:f,result:a}=Dn(b);if(a){let N={success:a.success,outputValues:a.outputValues,logs:a.logs,stdout:f,stderr:h,exitCode:_,durationMs:g};return a.error!==null&&(N.error=a.error),a.httpRequests&&(N.httpRequests=a.httpRequests),N}let v={success:!1,outputValues:{},logs:[],stdout:f,stderr:h,exitCode:_,durationMs:g};return _!==0&&(v.error=`Process exited with code ${String(_)}`),v}finally{r&&Zt(r,{recursive:!0,force:!0})}}var zn=2*1024*1024,Mn=new Set([".DS_Store",".gitignore"]),Hn=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".json",".md",".mdx",".yml",".yaml",".toml",".css",".scss",".html",".htm",".xml",".svg",".txt",".sh",".py",".env",".gitignore",".prettierrc",".eslintrc"]);function qn(e){if(e===void 0||e==="")return!1;let t=e.startsWith("[")?e.slice(0,e.indexOf("]")+1):e.split(":")[0]??"";return t==="localhost"||t==="127.0.0.1"||t==="[::1]"}var Un=["kizen.com","kizen.dev"];function Vn(e){let t;try{t=new URL(e)}catch{return!1}if(t.protocol!=="https:")return!1;let i=t.hostname.toLowerCase();return Un.some(n=>i===n||i.endsWith("."+n))}function Jn(e){if(e===void 0||e==="")return!1;try{let{hostname:t}=new URL(e);return t==="localhost"||t==="127.0.0.1"||t==="::1"}catch{return!1}}async function nr(e,t){if(t.includes("\0"))return null;let i=t.replace(/^\/+/,""),n=On(e,i);if(n!==e&&!n.startsWith(e+bt))return null;try{let r=await jn(n);return r!==e&&!r.startsWith(e+bt)?null:r}catch{return null}}function or(e,t){let i=t===e?"":t.slice(e.length+1);return i===""?!1:i.split(bt).some(r=>$e.has(r))}function ir(e){if(e.kind==="info")return e.message;let t=e.fromCache?" [cache]":"";return`${e.method} ${e.url} \u2192 ${String(e.status)}${t}`}function sr(){let e=$n(import.meta.url);return De(An(e),"viewer")}async function vt(e){try{return await Nn(e),!0}catch{return!1}}function ar(e,t,i,n,r,c,S,y,_,b){let h=Wt({onChange:y}),g=Rn(b);return(f,a)=>{(async()=>{let v=f.url??"/";try{if(!qn(f.headers.host)){a.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Forbidden"}));return}if((f.method==="POST"||f.method==="PATCH"||f.method==="DELETE")&&!Jn(f.headers.origin)){a.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Forbidden"}));return}if(t(`Received request: ${v}`),v==="/api/credentials"){a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end(n.current!==null?JSON.stringify(n.current):"{}");return}if(v==="/api/credential-profiles"&&f.method==="GET"){let s=await Ee();a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({profiles:s,active:r.current}));return}if(v==="/api/credentials/switch"&&f.method==="POST"){let s=[];for await(let p of f)s.push(p);let l=JSON.parse(Buffer.concat(s).toString("utf-8")||"{}").profile,d=l===void 0||l===""?await Y():await be(l);if(d!==null){n.current=d,r.current=l??void 0;let p=r.current;await ue(c,{credentialMode:"global",...p!==void 0&&{activeCredentialProfile:p}}),S({type:"credentials-updated",credentials:d})}a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({ok:d!==null}));return}if(v==="/api/bundle"){let s=De(process.cwd(),".kizenapp","bundle.json");try{let o=await Fn(s,"utf-8");a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end(o)}catch{a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end("{}")}return}if(v==="/api/last-path"&&f.method==="POST"){let s=[];for await(let l of f)s.push(l);let o=JSON.parse(Buffer.concat(s).toString("utf-8")||"{}");if(typeof o.path=="string"){_.current=o.path;let l=await Ie(c);await ue(c,{...l,lastPath:o.path})}a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end('{"ok":true}');return}if(v==="/api/execute-step"&&f.method==="POST"){let s=[];for await(let p of f)s.push(p);let o=JSON.parse(Buffer.concat(s).toString("utf-8")||"{}");if(typeof o.script!="string"){a.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Missing required field: script"}));return}let l={script:o.script,scriptRuntime:o.scriptRuntime??"python-3.9",inputs:o.inputs??{},secrets:o.secrets??{},onInstallProgress:p=>{p.kind==="start"?S({type:"venv-install-start"}):p.kind==="log"?S({type:"venv-install-log",line:p.line,stream:p.stream}):p.kind==="complete"?S({type:"venv-install-complete"}):S({type:"venv-install-error",message:p.message})}};typeof o.timeout=="number"&&o.timeout>0&&(l.timeout=Math.min(o.timeout,300*1e3));let d=await Qt(l);a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify(d));return}if(v==="/api/proxy-cache/clear"){h.clear(),i({kind:"info",message:"Proxy cache cleared"}),a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end('{"ok":true}');return}if(v.startsWith("/api/proxy")){let s=f.headers["x-proxy-target"];if(typeof s!="string"){a.writeHead(400),a.end("Missing x-proxy-target header");return}if(!Vn(s)){a.writeHead(400),a.end("Invalid proxy target");return}let o=v.slice(10)||"/",l=`${s}${o}`,d=f.method??"GET",p=[];for await(let pe of f)p.push(pe);let{host:F,"x-proxy-target":k,...I}=f.headers;if(d==="GET"){let pe=`GET::${l}`,{response:fe,fromCache:Ce}=await h.get(pe,()=>fetch(l,{method:"GET",headers:I}));i({kind:"request",method:"GET",status:fe.status,fromCache:Ce,url:o}),a.writeHead(fe.status,fe.headers),a.end(fe.body);return}let O=p.length>0?Buffer.concat(p):void 0,U=O&&O.length>0?O:void 0,J=await fetch(l,{method:d,headers:I,...U!==void 0&&{body:U}}),G=ft(Object.fromEntries(J.headers)),tt=await gt(J,mt);i({kind:"request",method:d,status:J.status,fromCache:!1,url:o}),a.writeHead(J.status,G),a.end(tt);return}if(v.startsWith("/api/source/tree")&&f.method==="GET"){let o=new URL(v,"http://localhost").searchParams.get("path")??"",l=await nr(g,o);if(l===null){a.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Invalid path"}));return}if(or(g,l)){a.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Path is excluded"}));return}try{let d=await Ln(l,{withFileTypes:!0}),p=await Promise.all(d.filter(k=>k.isDirectory()?!$e.has(k.name):k.isFile()?!Mn.has(k.name):!1).map(async k=>{let I=k.isDirectory()?"dir":"file",O={name:k.name,kind:I};if(I==="file")try{let U=await tr(De(l,k.name));O.size=U.size}catch{}return O}));p.sort((k,I)=>k.kind!==I.kind?k.kind==="dir"?-1:1:k.name.localeCompare(I.name));let F=l===g?"":l.slice(g.length+1);a.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({path:F,entries:p}))}catch(d){let p=d instanceof Error?d.message:String(d);t(`source/tree failed (${o}): ${p}`),a.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Not found"}))}return}if(v.startsWith("/api/source/file")&&f.method==="GET"){let o=new URL(v,"http://localhost").searchParams.get("path")??"";if(o===""){a.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Missing path"}));return}let l=await nr(g,o);if(l===null){a.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Invalid path"}));return}if(or(g,l)){a.writeHead(403,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Path is excluded"}));return}try{let d=await tr(l);if(!d.isFile()){a.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Not a file"}));return}if(d.size>zn){a.writeHead(413,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"File too large",size:d.size}));return}let p=rr(l).toLowerCase(),F=yt[p],k=Hn.has(p),I=F??(k?"text/plain; charset=utf-8":"application/octet-stream");a.writeHead(200,{"Content-Type":I,"Content-Length":String(d.size)}),er(l).pipe(a)}catch(d){let p=d instanceof Error?d.message:String(d);t(`source/file failed (${o}): ${p}`),a.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),a.end(JSON.stringify({error:"Not found"}))}return}let C=De(e,v==="/"?"/index.html":v),m=await vt(C)?C:De(e,"index.html"),u=rr(m),w=yt[u]??"application/octet-stream";a.writeHead(200,{"Content-Type":w}),er(m).pipe(a)}catch(N){if(v.startsWith("/api/proxy")){let E=v.slice(10)||"/",C=f.method??"GET";N instanceof ke?i({kind:"info",message:`${C} ${E} \u2192 502 (response exceeded ${String(N.limit)} bytes)`}):i({kind:"request",method:C,status:502,fromCache:!1,url:E})}else i({kind:"info",message:`Error handling ${v}: ${N instanceof Error?N.message:String(N)}`});a.headersSent||(a.writeHead(502),a.end("Bad Gateway"))}})()}}import{launch as Wn}from"chrome-launcher";import{createRequire as Kn}from"module";import{mkdirSync as Gn}from"fs";import{readFile as Xn}from"fs/promises";import{dirname as Yn,join as Zn}from"path";import{fileURLToPath as Qn}from"url";var lr=Kn(import.meta.url)("chrome-remote-interface"),eo=new Set(["content-security-policy","content-security-policy-report-only","x-frame-options"]),dr=["kizen.dev","kizen.com"];function cr(e){try{let{hostname:t}=new URL(e);return dr.some(i=>t===i||t.endsWith("."+i))}catch{return!1}}function Ge(e){if(e.type==="number")return Number(e.value);if(e.type==="boolean")return e.value==="true";if(e.type!=="undefined"){if(e.type==="object"&&e.value==="null")return null;if(e.type==="object"&&e.valuePreview){let{valuePreview:t}=e;if(t.subtype==="array")return t.properties.filter(n=>/^\d+$/.test(n.name)).sort((n,r)=>parseInt(n.name)-parseInt(r.name)).map(Ge);let i={};for(let n of t.properties)i[n.name]=Ge(n);return i}return e.value??`[${e.type}]`}}function to(e){if(e.value!==void 0)return e.value;if(e.preview){let{preview:t}=e;if(t.subtype==="array")return t.properties.filter(n=>/^\d+$/.test(n.name)).sort((n,r)=>parseInt(n.name)-parseInt(r.name)).map(Ge);let i={};for(let n of t.properties)i[n.name]=Ge(n);return i}return e.description??`[${e.type}]`}async function ro(e,t){if(t.type==="object"&&t.objectId)try{let n=(await e.Runtime.callFunctionOn({objectId:t.objectId,functionDeclaration:'function() { const seen = new WeakSet(); return JSON.stringify(this, function(k,v) { if (typeof v === "object" && v !== null) { if (seen.has(v)) return "[Circular]"; seen.add(v); } return v; }); }',returnByValue:!0})).result.value;if(typeof n=="string")return JSON.parse(n)}catch{}return to(t)}async function no(e){try{let t=Zn(Yn(Qn(import.meta.url)),"viewer","icon.png"),i=await Xn(t);await e.Browser.setDockTile({image:i.toString("base64")})}catch{}}async function ur(e){Gn(e.userDataDir,{recursive:!0});let t=`http://localhost:${String(e.port)}`,i=await Wn({startingUrl:t,chromeFlags:[`--app=${t}`,"--no-first-run","--no-default-browser-check","--disable-extensions"],userDataDir:e.userDataDir,handleSIGINT:!1,logLevel:"silent"}),n=i.port,r=null,c=null,S=null,y=null,_=0,b=!1,h=u=>{e.debugLog?.(u)},g=u=>{e.verbose&&e.debugLog?.(u)},f=()=>{c!==null&&(clearInterval(c),c=null)},a=u=>u==="chrome://new-tab-page/"||u==="chrome://newtab/",v=async u=>{f(),S=null,await no(u),await u.Fetch.enable({patterns:dr.flatMap(s=>[{urlPattern:`*://${s}/*`,requestStage:"Response"},{urlPattern:`*://*.${s}/*`,requestStage:"Response"}])}),u.on("Fetch.requestPaused",s=>{(async()=>{let o=s,{requestId:l,request:d,responseHeaders:p,responseStatusCode:F}=o,k=d.url;g(`Fetch ${String(F??"?")} ${k}`);let I=[],O=!1;for(let U of p??[])if(!eo.has(U.name.toLowerCase()))if(U.name.toLowerCase()==="set-cookie"&&cr(k)){let J=U.value.split(`
|
|
172
|
+
`).map(G=>/SameSite=/i.test(G)?G.replace(/SameSite=\w+/i,"SameSite=None"):G+"; SameSite=None; Secure").join(`
|
|
173
|
+
`);I.push({name:U.name,value:J}),O=!0}else I.push(U);O&&g(` rewrote Set-Cookie SameSite on ${k}`);try{let{body:U,base64Encoded:J}=await u.Fetch.getResponseBody({requestId:l});await u.Fetch.fulfillRequest({requestId:l,responseCode:F??200,responseHeaders:I,body:U,...J&&{base64Encoded:!0}})}catch(U){g(` fulfillRequest failed (${U.message}), falling through`);try{await u.Fetch.continueRequest({requestId:l})}catch(J){g(` continueRequest also failed (${J.message})`)}}})()}),c=setInterval(()=>{(async()=>{try{let{cookies:s}=await u.Network.getAllCookies(),o=0;for(let l of s){if(!cr(`https://${l.domain??""}`)||l.sameSite==="None")continue;let d=l.domain??"",p=d.startsWith(".")?d.slice(1):d;await u.Network.setCookie({url:`https://${p}`,name:l.name,value:l.value,domain:d,path:l.path??"/",secure:!0,...l.httpOnly!==void 0&&{httpOnly:l.httpOnly},...l.expirationDate!==void 0&&{expires:l.expirationDate},sameSite:"None"}),o++}o>0&&g(`cookie-patch cycle: updated ${String(o)} cookie(s) to SameSite=None`)}catch(s){g(`cookie-patch cycle failed: ${s.message}`)}})()},2e3),await u.send("Runtime.enable",{generatePreview:!0}),u.on("Target.attachedToTarget",s=>{let o=s,{sessionId:l,targetInfo:d}=o;g(`attached session ${l} (${d.type} ${d.url})`),u.send("Runtime.enable",{generatePreview:!0},l).catch(p=>{g(` Runtime.enable on session ${l} failed: ${p.message}`)})}),await u.Target.setAutoAttach({autoAttach:!0,waitForDebuggerOnStart:!1,flatten:!0}),await u.Target.setDiscoverTargets({discover:!0});let w=s=>{if(s.type==="page"){if(a(s.url)){h(`closed stray new-tab-page target ${s.targetId}`),S!==null&&u.Target.activateTarget({targetId:S}).catch(()=>{}),u.Target.closeTarget({targetId:s.targetId}).catch(()=>{});return}S===null&&(S=s.targetId,h(`captured main target ${s.targetId} (${s.url})`))}};u.on("Target.targetCreated",s=>{let{targetInfo:o}=s;g(`target +${o.type} ${o.targetId} ${o.url}`),w(o)}),u.on("Target.targetInfoChanged",s=>{let{targetInfo:o}=s;g(`target ~${o.type} ${o.targetId} ${o.url}`),w(o)}),u.on("Runtime.consoleAPICalled",s=>{(async()=>{let{type:o,args:l}=s;if(l[0]?.type==="string"&&String(l[0].value).includes("Chrome Security"))return;let d=o==="warning"?"warn":["log","warn","error","info"].includes(o)?o:"log";g(`console.${d} (${String(l.length)} arg(s))`);let p=await Promise.all(l.map(F=>ro(u,F)));e.broadcast({type:"console-message",level:d,args:p})})().catch(o=>{g(`console serialization failed: ${o.message}`)})}),u.on("disconnect",()=>{b||r!==u||(h("CDP disconnected"),r=null,f(),N())})},N=()=>{if(b||y!==null)return;let u=Math.min(1e3*2**_,1e4),w=_+1;_++,h(`CDP reconnect scheduled in ${String(u)}ms (attempt #${String(w)})`),y=setTimeout(()=>{y=null,(async()=>{let s;try{s=await lr({port:n})}catch{h(`CDP reconnect attempt #${String(w)} failed`),N();return}if(b){try{await s.close()}catch{}return}r=s;try{await v(s),h(`CDP reconnected after ${String(w)} attempt(s)`),_=0}catch{r===s&&(r=null);try{await s.close()}catch{}h(`CDP reconnect attempt #${String(w)} failed during setup`),N()}})()},u)},E=30,C=300;for(let u=0;u<E;u++){await new Promise(w=>setTimeout(w,C)),h(`CDP connect attempt ${String(u+1)}/${String(E)}`);try{let w=await lr({port:n});r=w,e.path&&w.send("Page.navigate",{url:`${t}${e.path}`});try{await v(w),h("CDP connected")}catch{r===w&&(r=null);try{await w.close()}catch{}h("CDP setup failed after connect")}break}catch{if(u===E-1){let w=`CDP unavailable after ${String(E)} attempts \u2014 viewer running without interception (CSP/cookie rewrites disabled)`;return h(w),e.onWarn?.(w),{kill:()=>{i.kill()}}}}}return r?{kill:()=>{if(h("CDP kill requested \u2014 tearing down"),b=!0,y!==null&&(clearTimeout(y),y=null),f(),r!==null){try{r.close()}catch{}r=null}i.kill()}}:{kill:()=>{i.kill()}}}import{useCallback as oo,useState as io}from"react";function Xe(e){let[t,i]=io([]),n=oo(r=>{i(c=>[...c,`${new Date().toLocaleTimeString()}: ${r}`].slice(-e))},[e]);return[t,n]}import{useCallback as Ye,useEffect as Ct,useState as Ze}from"react";import{Box as z,Text as D,useInput as so}from"ink";import{jsx as P,jsxs as H}from"react/jsx-runtime";var ne=["apiKey","userId","businessId"],ao={apiKey:"API Key",userId:"User ID",businessId:"Business ID"},Re=({text:e})=>P(D,{dimColor:!0,children:e}),Qe=({initialMode:e,showProfileManager:t,onComplete:i,onCancel:n})=>{let[r,c]=Ze(e==="global"&&!t?{type:"loading"}:e==="global"&&t?{type:"profile-loading"}:{type:"mode-select",cursor:0}),[S,y]=Ze(""),[_,b]=Ze(null),[h,g]=Ze(void 0),f=Ye(s=>{if(s==="local"){i({mode:"local",credentials:null});return}c({type:"profile-loading"})},[i]),a=Ye(async s=>{if(s===null){c({type:"profile-loading"});let d=await Ee();c({type:"name-entry",nameBuffer:"",existingProfiles:d});return}g(s.isDefault?void 0:s.name),c({type:"loading"});let o=s.isDefault?await Y():await be(s.name),l=o?Math.max(0,Z.indexOf(o.environment)):0;c({type:"creds-entry",field:0,values:o??{},envCursor:l})},[]),v=Ye(async(s,o)=>{let l=Z[o]??"go",d={apiKey:s.apiKey??"",userId:s.userId??"",businessId:s.businessId??"",environment:l};c({type:"saving"});try{await Vt(h??re,d),i({mode:"global",credentials:d,...h!==void 0&&{profileName:h}})}catch(p){b(p instanceof Error?p.message:String(p)),c({type:"creds-entry",field:0,values:s,envCursor:o})}},[i,h]);so((s,o)=>{if(o.ctrl&&s==="c"&&process.exit(0),o.escape){if((r.type==="creds-entry"||r.type==="name-entry")&&e==="global"&&t){c({type:"profile-loading"}),N();return}n?.();return}if(r.type==="mode-select"){if(o.upArrow)c({type:"mode-select",cursor:0});else if(o.downArrow)c({type:"mode-select",cursor:1});else if(o.return){let l=r.cursor===0?"global":"local";f(l)}return}if(r.type==="profile-select"){let{profiles:l,cursor:d}=r,p=l.length,F=l.length+1;if(o.upArrow)c({...r,cursor:(d-1+F)%F});else if(o.downArrow)c({...r,cursor:(d+1)%F});else if(o.return)if(d===p)c({type:"name-entry",nameBuffer:"",existingProfiles:l});else{let k=l[d]??l[0];k&&a(k)}return}if(r.type==="name-entry"){let{nameBuffer:l,existingProfiles:d}=r;if(o.backspace||o.delete)c({type:"name-entry",nameBuffer:l.slice(0,-1),existingProfiles:d});else if(o.return){let p=l.trim();if(!p){c({...r,nameError:"Name cannot be empty"});return}if(p===re){c({...r,nameError:`"${re}" is reserved`});return}if(d.some(F=>F.name===p)){c({...r,nameError:`Profile "${p}" already exists`});return}g(p),c({type:"creds-entry",field:0,values:{},envCursor:0}),y("")}else s&&!o.ctrl&&!o.meta&&c({type:"name-entry",nameBuffer:l+s,existingProfiles:d});return}if(r.type==="creds-entry"){let{field:l,values:d,envCursor:p}=r;if(l===ne.length){o.leftArrow?c({...r,envCursor:(p-1+Z.length)%Z.length}):o.rightArrow?c({...r,envCursor:(p+1)%Z.length}):o.upArrow?(c({...r,field:l-1}),y(d[ne[l-1]]??"")):o.return&&v(d,p);return}let k=ne[l];if(!k)return;if(o.backspace||o.delete)y(I=>I.slice(0,-1));else if(o.upArrow&&l>0){let I={...d,[k]:S},O=l-1;c({...r,field:O,values:I}),y(I[ne[O]]??"")}else if(o.return||o.tab||o.downArrow){let I={...d,[k]:S},O=l+1;c({...r,field:O,values:I}),O<ne.length?y(I[ne[O]]??""):y("")}else s&&!o.ctrl&&!o.meta&&y(I=>I+s)}});let N=Ye(async()=>{let s=await Ee();c({type:"profile-select",profiles:s,cursor:0})},[]);if(Ct(()=>{r.type==="profile-loading"&&N()},[r.type,N]),Ct(()=>{e==="global"&&!t&&(async()=>{let s=await Y(),o=s?Math.max(0,Z.indexOf(s.environment)):0;c({type:"creds-entry",field:0,values:s??{},envCursor:o})})()},[]),Ct(()=>{r.type==="creds-entry"&&r.field===0&&y(r.values.apiKey??"")},[r.type]),r.type==="loading"||r.type==="saving"||r.type==="profile-loading"){let s=r.type==="saving"?"Saving credentials\u2026":r.type==="profile-loading"?"Loading profiles\u2026":"Loading credentials\u2026";return P(z,{paddingY:1,paddingX:2,children:P(D,{dimColor:!0,children:s})})}if(r.type==="done")return null;if(r.type==="mode-select")return H(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[P(V,{}),H(z,{flexDirection:"column",gap:0,children:[P(D,{bold:!0,children:"Credential mode"}),[{label:"Global",desc:"~/.kizenappbuilder/credentials.json",mode:"global"},{label:"Local",desc:"Enter credentials in the browser dev tools",mode:"local"}].map((o,l)=>{let d=r.cursor===l;return H(z,{gap:2,children:[P(D,{...d&&{color:"cyan"},children:d?"\u276F":" "}),P(D,{bold:d,...d&&{color:"cyan"},children:o.label}),P(D,{dimColor:!0,children:o.desc})]},o.mode)})]}),P(Re,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit"})]});if(r.type==="profile-select"){let{profiles:s,cursor:o}=r,l=s.length;return H(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[P(V,{}),H(z,{flexDirection:"column",gap:0,children:[P(D,{bold:!0,children:"Credential profiles"}),s.map((d,p)=>{let F=o===p;return H(z,{gap:2,children:[P(D,{...F&&{color:"cyan"},children:F?"\u276F":" "}),P(D,{bold:F,...F&&{color:"cyan"},children:d.isDefault?"Default":d.name}),P(D,{dimColor:!0,children:d.path})]},d.name)}),H(z,{gap:2,children:[P(D,{...o===l&&{color:"cyan"},children:o===l?"\u276F":" "}),P(D,{bold:o===l,...o===l&&{color:"cyan"},children:"+ Add new profile"})]})]}),P(Re,{text:"\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel"})]})}if(r.type==="name-entry"){let{nameBuffer:s,nameError:o}=r;return H(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[P(V,{}),P(z,{flexDirection:"column",gap:0,children:P(D,{bold:!0,children:"New credential profile"})}),o&&H(D,{color:"red",children:["Error: ",o]}),H(z,{gap:2,children:[P(z,{width:12,children:P(D,{bold:!0,color:"cyan",children:"Profile name"})}),P(D,{color:"cyan",children:">"}),P(D,{children:s}),P(D,{color:"cyan",children:"\u2588"})]}),P(Re,{text:"Type a name \xB7 Enter to confirm \xB7 Esc to go back"})]})}let{field:E,values:C,envCursor:m}=r,u=E===ne.length,w=h??re;return H(z,{flexDirection:"column",paddingY:1,paddingX:2,gap:1,children:[P(V,{}),H(z,{flexDirection:"column",gap:0,children:[P(D,{bold:!0,children:h!==void 0?`Profile: ${h}`:"Global credentials"}),H(D,{dimColor:!0,children:["Saved to:"," ",h!==void 0?`~/.kizenappbuilder/${h}.json`:xe]})]}),_&&H(D,{color:"red",children:["Error: ",_]}),H(z,{flexDirection:"column",gap:0,children:[ne.map((s,o)=>{let l=E===o,d=l?S:C[s]??"";return H(z,{gap:2,children:[P(z,{width:12,children:P(D,{bold:l,...l&&{color:"cyan"},children:ao[s]})}),P(D,{...l&&{color:"cyan"},children:">"}),P(D,{children:d}),l&&P(D,{color:"cyan",children:"\u2588"})]},s)}),H(z,{gap:2,children:[P(z,{width:12,children:P(D,{bold:u,...u&&{color:"cyan"},children:"Environment"})}),P(D,{...u&&{color:"cyan"},children:">"}),P(z,{gap:1,children:Z.map((s,o)=>{let l=o===m;return u?P(D,{bold:l,...l&&{color:"cyan"},children:l?`[${s}]`:s},s):P(D,{dimColor:!l,bold:l,children:s},s)})})]})]}),u?P(Re,{text:"\u2190\u2192 to select \xB7 \u2191\u2193 to move \xB7 Enter to save \xB7 Esc to cancel"}):P(Re,{text:"\u2191\u2193 to move \xB7 Backspace to delete \xB7 Esc to cancel"}),H(D,{dimColor:!0,children:["Profile: ",w]})]})};import{jsx as L,jsxs as $}from"react/jsx-runtime";var mo=[".kizenapp",".git"],fr=({port:e,pluginDir:t,outputDir:i,credentials:n,credentialMode:r,activeCredentialProfile:c,lastPath:S,debug:y=!1,verbose:_=!1})=>{let b=Q(n),h=Q(c),g=Q(S),[f,a]=W("main"),[v,N]=W(r),[E,C]=W("starting"),[m,u]=W(null),[w,s]=Xe(50),[o,l]=Xe(50),[d,p]=Xe(50),[F,k]=W([]),[I,O]=W("pending"),[U,J]=W(null),[G,tt]=W(null),[pe,fe]=W(null),[Ce,rt]=W(0),[wt,xr]=W(0),[br,vr]=W(0),[Cr,wr]=W(0),nt=Q(!1),St=Q(!1),Fe=Q(null),we=Q(null),ee=Q(new Set),je=Q([]),K=ve(x=>{let j=JSON.stringify(x);if(ee.current.size===0){je.current=[...je.current,j].slice(-50);return}let te=!1;for(let X of ee.current)if(X.readyState===1)try{X.send(j)}catch{ee.current.delete(X),te=!0}te&&rt(ee.current.size)},[]),Ae=s,ot=ve(()=>{Fe.current?.kill(),Fe.current=null,ur({port:e,userDataDir:pr(i,".chrome"),broadcast:K,onWarn:x=>{s(`\u26A0 ${x}`)},...g.current!==void 0&&{path:g.current},...y&&{debugLog:p},..._&&{verbose:!0}}).then(x=>{Fe.current=x})},[e,i,K,y,_,p,s]);et(()=>{let x=()=>{Fe.current?.kill()};return process.on("exit",x),()=>{process.off("exit",x)}},[]),co((x,j)=>{(x==="q"||j.ctrl&&x==="c")&&process.exit(0),x==="c"&&a("editing"),x==="v"&&ot()},{isActive:f==="main"}),et(()=>{E!=="running"||St.current||(St.current=!0,ot())},[E,ot]);let Sr=ve(x=>{b.current=x.credentials,h.current=x.profileName,a("main"),N(x.mode);let j=x.profileName;ue(i,{credentialMode:x.mode,...j!==void 0&&{activeCredentialProfile:j}}),K({type:"credentials-updated",credentials:x.credentials})},[i,K]),Pt=ve(x=>{k(j=>[...j,`${new Date().toLocaleTimeString()}: ${ir(x)}`].slice(-50)),x.kind==="request"&&(x.fromCache?vr(j=>j+1):wr(j=>j+1)),K({type:"proxy-log",entry:x})},[K]),ie=ve(x=>{l(x),K({type:"log",message:x})},[l,K]),it=ve(()=>{nt.current||(nt.current=!0,O("building"),J(null),ie("Build started"),ze(t,i).then(({bundleSize:x})=>{ie(`Build finished \u2014 bundle: ${Se(x)}`),O("done"),tt(new Date),fe(x),ie("Notifying viewers to reload"),K({type:"rebuild"})}).catch(x=>{let j=x instanceof Error?x.message:String(x);J(j),O("error")}).finally(()=>{nt.current=!1}))},[t,i,ie,K]);if(et(()=>{it();let x=uo(t,{recursive:!0},(j,te)=>{if(!te)return;let X=te.replace(/\\/g,"/");mo.some(Oe=>X.startsWith(Oe))||(ie(`File change detected: ${te}`),we.current!==null&&clearTimeout(we.current),we.current=setTimeout(it,150))});return x.on("error",j=>{ie(`File watcher error \u2014 rebuilds have stopped: ${j.message}`)}),()=>{x.close(),we.current!==null&&clearTimeout(we.current)}},[t,it,ie]),et(()=>{let x=sr();vt(pr(x,"index.html")).then(j=>{if(!j){u("Viewer not built. Run 'pnpm build:viewer' first."),C("error");return}let te=ar(x,Ae,Pt,b,h,i,K,xr,g,t),X=po(te),Oe=new fo({server:X});return Oe.on("connection",se=>{for(let Tr of je.current)se.readyState===1&&se.send(Tr);je.current=[],Ae("Viewer connected for live reload"),ee.current.add(se),rt(ee.current.size),se.on("close",()=>{ee.current.delete(se),rt(ee.current.size)})}),X.listen(e,"127.0.0.1",()=>{C("running"),Ae(`Server started on 127.0.0.1:${String(e)}`)}),X.on("error",se=>{u(se.message),C("error")}),()=>{Oe.close(),X.close()}})},[e,Ae,Pt,K,i,t]),f==="editing")return L(Qe,{...v!==void 0&&{initialMode:v},showProfileManager:v==="global",onComplete:Sr,onCancel:()=>{a("main")}});let st=G!==null?Math.round((Date.now()-G.getTime())/1e3):null,Pr=st===null?"":st<5?" (just now)":` (${String(st)}s ago)`;return $(q,{flexDirection:"column",paddingY:1,paddingX:2,children:[L(V,{marginBottom:1}),$(q,{flexDirection:"column",children:[$(q,{gap:2,marginBottom:0,children:[L(R,{bold:!0,dimColor:!0,children:"App Build"}),I==="pending"&&L(R,{dimColor:!0,children:"waiting\u2026"}),I==="building"&&$(q,{gap:1,children:[L(Me,{}),L(R,{children:"Building plugin package\u2026"})]}),I==="done"&&$(q,{gap:1,children:[L(R,{color:"green",children:"\u2713 Built"}),G!==null&&$(R,{dimColor:!0,children:["at ",G.toLocaleTimeString(),Pr]}),pe!==null&&$(R,{dimColor:!0,children:["\xB7 bundle: ",Se(pe)]})]}),I==="error"&&$(R,{color:"red",children:["\u2717 ",U??"unknown error"]})]}),L(q,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:o.slice(-8).map((x,j)=>L(R,{dimColor:!0,wrap:"truncate",children:x},j))})]}),$(q,{marginTop:1,flexDirection:"column",children:[$(q,{gap:2,children:[L(R,{bold:!0,dimColor:!0,children:"Web Server"}),E==="starting"&&L(R,{dimColor:!0,children:"starting\u2026"}),E==="running"&&$(q,{gap:2,children:[L(R,{color:"green",children:"\u2713 Running"}),$(R,{dimColor:!0,children:["\u25CF ",Ce," viewer",Ce!==1?"s":""]})]}),E==="error"&&$(R,{color:"red",children:["\u2717 ",m??"Server error"]})]}),L(q,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:w.slice(-8).map((x,j)=>L(R,{dimColor:!0,wrap:"truncate",children:x},j))})]}),$(q,{marginTop:1,flexDirection:"column",children:[$(q,{gap:2,children:[L(R,{bold:!0,dimColor:!0,children:"Network Proxy"}),L(R,{color:"green",children:"\u2713 Active"}),$(R,{dimColor:!0,children:["\u25CF ",wt," key",wt!==1?"s":""," in the network cache \xB7"," ",br," hit \xB7 ",Cr," miss"]})]}),L(q,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:F.length===0?L(R,{dimColor:!0,children:" No requests yet."}):F.slice(-8).map((x,j)=>L(R,{dimColor:!0,wrap:"truncate",children:x},j))})]}),y&&$(q,{marginTop:1,flexDirection:"column",children:[$(q,{gap:2,children:[L(R,{bold:!0,dimColor:!0,children:"CDP Debug"}),$(R,{dimColor:!0,children:["\u25CF ",d.length," event(s)"]})]}),L(q,{flexDirection:"column",height:10,borderStyle:"single",borderColor:"gray",overflow:"hidden",children:d.length===0?L(R,{dimColor:!0,children:" No CDP events yet."}):d.slice(-8).map((x,j)=>L(R,{dimColor:!0,wrap:"truncate",children:x},j))})]}),$(q,{marginTop:1,gap:1,children:[L(R,{dimColor:!0,children:"["}),L(R,{children:"q"}),L(R,{dimColor:!0,children:"] quit ["}),L(R,{children:"c"}),L(R,{dimColor:!0,children:"] credentials ["}),L(R,{children:"v"}),$(R,{dimColor:!0,children:["] ",Ce===0?"open viewer":"relaunch viewer"]})]})]})};async function mr(e){return new Promise(t=>{let i=null,n=c=>{i?.(),t(c)},{unmount:r}=yr(gr(Qe,{...e!==void 0&&{initialMode:e},onComplete:n}),{exitOnCtrlC:!1});i=r})}function hr(e){e.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(async t=>{let i=parseInt(t.port,10),n=process.cwd(),r=`${n}/.kizenapp`;ye(n);let c=null,S,y,{lastPath:_}=await Ie(r);if(t.credentials)c=await Je(t.credentials);else{let f=await Ie(r);if(f.credentialMode==="local")S="local";else if(f.credentialMode==="global"){if(S="global",y=f.activeCredentialProfile,y?(c=await be(y),c||(c=await Y(),y=void 0)):c=await Y(),!c){let a=await mr("global");c=a.credentials,y=a.profileName}}else{let a=await mr();c=a.credentials,S=a.mode,y=a.profileName;let v=a.profileName;await ue(r,{credentialMode:a.mode,...v!==void 0&&{activeCredentialProfile:v}})}}let b=t.verbose===!0,h=b||t.debug===!0,{waitUntilExit:g}=yr(gr(fr,{port:i,pluginDir:n,outputDir:r,credentials:c,...S!==void 0&&{credentialMode:S},...y!==void 0&&{activeCredentialProfile:y},..._!==void 0&&{lastPath:_},debug:h,verbose:b}),{exitOnCtrlC:!1});await g()})}var bo=ho(yo(xo(import.meta.url)),"..","package.json"),vo=JSON.parse(go(bo,"utf-8"));Le.name("appbuilder").description("Kizen plugin app builder").version(vo.version);Jt(Le);Ft(Le);hr(Le);Le.parse();
|
|
2840
174
|
//# sourceMappingURL=index.js.map
|