@kizenapps/cli 0.2.0 → 0.4.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.
Files changed (85) hide show
  1. package/dist/chunk-4OYGMEFM.js +3675 -0
  2. package/dist/chunk-4OYGMEFM.js.map +1 -0
  3. package/dist/chunk-4Z6S6LWP.js +6134 -0
  4. package/dist/chunk-4Z6S6LWP.js.map +1 -0
  5. package/dist/chunk-5WPGAZLP.js +6136 -0
  6. package/dist/chunk-5WPGAZLP.js.map +1 -0
  7. package/dist/chunk-6XGFZ4W5.js +3673 -0
  8. package/dist/chunk-6XGFZ4W5.js.map +1 -0
  9. package/dist/chunk-HJPLDI45.js +6134 -0
  10. package/dist/chunk-HJPLDI45.js.map +1 -0
  11. package/dist/chunk-KMKDY2I6.js +46 -0
  12. package/dist/chunk-KMKDY2I6.js.map +1 -0
  13. package/dist/chunk-LSHHXCC6.js +3639 -0
  14. package/dist/chunk-LSHHXCC6.js.map +1 -0
  15. package/dist/chunk-NNDSADQG.js +3641 -0
  16. package/dist/chunk-NNDSADQG.js.map +1 -0
  17. package/dist/chunk-SEGVTWSK.js +44 -0
  18. package/dist/chunk-SEGVTWSK.js.map +1 -0
  19. package/dist/chunk-ZWE3DS7E.js +39 -0
  20. package/dist/chunk-ZWE3DS7E.js.map +1 -0
  21. package/dist/devtools-HNR7DD7Q.js +68 -0
  22. package/dist/devtools-HNR7DD7Q.js.map +1 -0
  23. package/dist/devtools-HW3UQAHH.js +69 -0
  24. package/dist/devtools-HW3UQAHH.js.map +1 -0
  25. package/dist/devtools-KXIXZGBY.js +64 -0
  26. package/dist/devtools-KXIXZGBY.js.map +1 -0
  27. package/dist/devtools-PDXM3L35.js +67 -0
  28. package/dist/devtools-PDXM3L35.js.map +1 -0
  29. package/dist/devtools-SDEASRYI.js +66 -0
  30. package/dist/devtools-SDEASRYI.js.map +1 -0
  31. package/dist/devtools-XBEEGBQ4.js +66 -0
  32. package/dist/devtools-XBEEGBQ4.js.map +1 -0
  33. package/dist/dist-4P5P64FK.js +8474 -0
  34. package/dist/dist-4P5P64FK.js.map +1 -0
  35. package/dist/dist-AVHDSU4X.js +137 -0
  36. package/dist/dist-AVHDSU4X.js.map +1 -0
  37. package/dist/dist-C3F6UOWF.js +8472 -0
  38. package/dist/dist-C3F6UOWF.js.map +1 -0
  39. package/dist/dist-NUJOSN7W.js +8472 -0
  40. package/dist/dist-NUJOSN7W.js.map +1 -0
  41. package/dist/dist-QHDMIBVE.js +135 -0
  42. package/dist/dist-QHDMIBVE.js.map +1 -0
  43. package/dist/dist-ZIZQOSY3.js +135 -0
  44. package/dist/dist-ZIZQOSY3.js.map +1 -0
  45. package/dist/electron/icon.png +0 -0
  46. package/dist/electron/main.js +53 -6
  47. package/dist/electron/main.js.map +1 -1
  48. package/dist/electron/preload.cjs +9 -0
  49. package/dist/gzip-size-K37OQCQ2.js +137 -0
  50. package/dist/gzip-size-K37OQCQ2.js.map +1 -0
  51. package/dist/gzip-size-QRGIYYEQ.js +137 -0
  52. package/dist/gzip-size-QRGIYYEQ.js.map +1 -0
  53. package/dist/gzip-size-SHZBT5GU.js +139 -0
  54. package/dist/gzip-size-SHZBT5GU.js.map +1 -0
  55. package/dist/index.js +577 -168
  56. package/dist/index.js.map +1 -1
  57. package/dist/viewer/assets/calendarSource.worker-C9iglnKD.js +24 -0
  58. package/dist/viewer/assets/expression.worker-Cin-WNVB.js +5 -0
  59. package/dist/viewer/assets/floatingFrame.worker-CWLtchq3.js +23 -0
  60. package/dist/viewer/assets/generic.worker-DRXu0-5B.js +23 -0
  61. package/dist/viewer/assets/index-BERinSUs.css +2 -0
  62. package/dist/viewer/assets/index-BExFCUNb.js +582 -0
  63. package/dist/viewer/assets/index-BTnaSAbf.js +582 -0
  64. package/dist/viewer/assets/index-BsYWmcLM.css +2 -0
  65. package/dist/viewer/assets/index-C9gGMSH0.js +582 -0
  66. package/dist/viewer/assets/index-CGp_x2sR.js +582 -0
  67. package/dist/viewer/assets/index-D1lc5mUR.js +582 -0
  68. package/dist/viewer/assets/index-DBXDPp8Y.css +2 -0
  69. package/dist/viewer/assets/index-DUFimvi2.js +582 -0
  70. package/dist/viewer/assets/index-DVM12um9.js +582 -0
  71. package/dist/viewer/assets/index-DiwdXOw0.css +2 -0
  72. package/dist/viewer/assets/index-DxGbfW9l.js +582 -0
  73. package/dist/viewer/assets/index-o7N1iv71.js +582 -0
  74. package/dist/viewer/assets/index-wLgWfI8W.js +582 -0
  75. package/dist/viewer/assets/recordDetail.worker-BIHPFt8Y.js +23 -0
  76. package/dist/viewer/icon.png +0 -0
  77. package/dist/viewer/index.html +2 -2
  78. package/package.json +17 -13
  79. package/dist/viewer/assets/calendarSource.worker-DF0wAwJR.js +0 -24
  80. package/dist/viewer/assets/expression.worker-_W4VR2xe.js +0 -5
  81. package/dist/viewer/assets/floatingFrame.worker-DXcDBfqJ.js +0 -23
  82. package/dist/viewer/assets/generic.worker-CsqlYndL.js +0 -23
  83. package/dist/viewer/assets/index-CW_UqFf3.css +0 -1
  84. package/dist/viewer/assets/index-ClCwhanM.js +0 -637
  85. package/dist/viewer/assets/recordDetail.worker-Ccm59Np6.js +0 -23
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { render } from "ink";
9
9
 
10
10
  // src/ui/BuildUI.tsx
11
11
  import { useEffect, useState } from "react";
12
- import { Box, Text, useApp } from "ink";
12
+ import { Box as Box2, Text as Text2, useApp } from "ink";
13
13
 
14
14
  // src/lib/runBuild.ts
15
15
  import { mkdir, writeFile } from "fs/promises";
@@ -83,8 +83,33 @@ async function runBuild(pluginDir, outputDir, onStep) {
83
83
  await writeFile(join2(outputDir, "bundle.json"), JSON.stringify(bundle, null, 2), "utf-8");
84
84
  }
85
85
 
86
+ // src/ui/Logo.tsx
87
+ import { Box, Text } from "ink";
88
+ import { jsx } from "react/jsx-runtime";
89
+ var LOGO_LINES = [
90
+ " @@@ ",
91
+ " @@@@@ @@@@@@@ ",
92
+ " @@@@ @@@@@@@@@@ ",
93
+ " @@@@ @@@@@ ",
94
+ " @@@ @@@@ @@@@@ @@@@@@@ ",
95
+ " @@@@ @@@@ @@@@ @@@@@@@@@@@@@",
96
+ " @@@@ @@@@ @@@@@",
97
+ " @@@@ @@@ @@@@ @@@",
98
+ " @@@@ @@@@@@ ",
99
+ " @@@@@@ @@@@ ",
100
+ " @@@ @@@@@ @@@ @@@@ ",
101
+ " @@@@@ @@@@ @@@@ ",
102
+ " @@@@@@@@@@@@@ @@@ @@@ @@@ ",
103
+ " @@@@@@@@ @@@@ @@@ @@@@ ",
104
+ " @@@@@ @@@@ ",
105
+ " @@@@@@@@@@@ @@@@ ",
106
+ " @@@@@@@ @@@@@ ",
107
+ " @@@@ "
108
+ ];
109
+ var Logo = () => /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: LOGO_LINES.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "cyan", children: line }, i)) });
110
+
86
111
  // src/ui/BuildUI.tsx
87
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
112
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
88
113
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
89
114
  var Spinner = () => {
90
115
  const [frame, setFrame] = useState(0);
@@ -96,7 +121,7 @@ var Spinner = () => {
96
121
  clearInterval(id);
97
122
  };
98
123
  }, []);
99
- return /* @__PURE__ */ jsx(Text, { color: "cyan", children: SPINNER_FRAMES[frame] ?? "\u280B" });
124
+ return /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: SPINNER_FRAMES[frame] ?? "\u280B" });
100
125
  };
101
126
  var STEPS = [
102
127
  "creating-dir",
@@ -113,57 +138,77 @@ var STEP_LABELS = {
113
138
  "writing-bundle": "Writing bundle.json"
114
139
  };
115
140
  var BuildUI = ({ outputDir, pluginDir }) => {
116
- const { exit } = useApp();
141
+ const app = useApp();
117
142
  const [step, setStep] = useState("creating-dir");
118
143
  const [errorMessage, setErrorMessage] = useState(null);
119
144
  useEffect(() => {
120
145
  void runBuild(pluginDir, outputDir, setStep).then(() => {
121
146
  setStep("done");
122
- exit();
147
+ app.exit();
123
148
  }).catch((err) => {
124
149
  const message = err instanceof Error ? err.message : String(err);
125
150
  setErrorMessage(message);
126
151
  setStep("error");
127
- exit(err instanceof Error ? err : new Error(message));
152
+ app.exit();
128
153
  });
129
- }, [outputDir, pluginDir, exit]);
154
+ }, [outputDir, pluginDir, app]);
130
155
  const currentIndex = STEPS.indexOf(step);
131
156
  const isError = step === "error";
132
157
  const isDone = step === "done";
133
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingY: 1, paddingX: 1, children: [
134
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
135
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "Kizen App Builder" }),
136
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(22) })
158
+ return /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", paddingY: 1, paddingX: 2, children: [
159
+ /* @__PURE__ */ jsx2(Logo, {}),
160
+ /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
161
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "Kizen App Builder" }),
162
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2500".repeat(24) })
137
163
  ] }),
138
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, children: [
164
+ /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", gap: 0, children: [
139
165
  STEPS.map((s, i) => {
140
166
  const isActive = step === s;
141
167
  const isStepDone = isDone || currentIndex > i;
142
168
  const isFailed = isError && i === currentIndex;
143
- return /* @__PURE__ */ jsx(Box, { gap: 1, children: isFailed ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
169
+ return /* @__PURE__ */ jsx2(Box2, { gap: 1, children: isFailed ? /* @__PURE__ */ jsxs(Text2, { color: "red", children: [
144
170
  "\u2717 ",
145
171
  STEP_LABELS[s]
146
- ] }) : isStepDone ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
172
+ ] }) : isStepDone ? /* @__PURE__ */ jsxs(Text2, { color: "green", children: [
147
173
  "\u2713 ",
148
174
  STEP_LABELS[s]
149
175
  ] }) : isActive ? /* @__PURE__ */ jsxs(Fragment, { children: [
150
- /* @__PURE__ */ jsx(Spinner, {}),
151
- /* @__PURE__ */ jsx(Text, { children: STEP_LABELS[s] })
152
- ] }) : /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
153
- "\xB7 ",
176
+ /* @__PURE__ */ jsx2(Spinner, {}),
177
+ /* @__PURE__ */ jsx2(Text2, { children: STEP_LABELS[s] })
178
+ ] }) : /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
179
+ " ",
154
180
  STEP_LABELS[s]
155
181
  ] }) }, s);
156
182
  }),
157
- errorMessage !== null && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: errorMessage }) })
183
+ errorMessage !== null && /* @__PURE__ */ jsx2(Box2, { marginTop: 1, borderStyle: "single", borderColor: "red", paddingX: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "red", children: errorMessage }) })
158
184
  ] })
159
185
  ] });
160
186
  };
161
187
 
188
+ // src/lib/gitignore.ts
189
+ import * as fs from "fs";
190
+ import * as path from "path";
191
+ function ensureGitignore(projectDir) {
192
+ const gitignorePath = path.join(projectDir, ".gitignore");
193
+ const entry = ".kizenapp/";
194
+ let contents = "";
195
+ if (fs.existsSync(gitignorePath)) {
196
+ contents = fs.readFileSync(gitignorePath, "utf8");
197
+ const lines = contents.split("\n").map((l) => l.trim());
198
+ if (lines.includes(entry) || lines.includes(".kizenapp")) {
199
+ return;
200
+ }
201
+ }
202
+ const addition = contents.endsWith("\n") ? entry + "\n" : "\n" + entry + "\n";
203
+ fs.writeFileSync(gitignorePath, contents + addition);
204
+ }
205
+
162
206
  // src/commands/build.ts
163
207
  function buildCommand(program2) {
164
208
  program2.command("build").description("Bundle the plugin app into .kizenapp directory").action(async () => {
165
- const outputDir = `${process.cwd()}/.kizenapp`;
166
209
  const pluginDir = process.cwd();
210
+ const outputDir = `${pluginDir}/.kizenapp`;
211
+ ensureGitignore(pluginDir);
167
212
  const { waitUntilExit } = render(createElement(BuildUI, { outputDir, pluginDir }));
168
213
  await waitUntilExit();
169
214
  });
@@ -174,24 +219,285 @@ import { createElement as createElement2 } from "react";
174
219
  import { render as render2 } from "ink";
175
220
 
176
221
  // src/ui/DevUI.tsx
177
- import { useCallback, useEffect as useEffect2, useRef, useState as useState2 } from "react";
178
- import { Box as Box2, Text as Text2, useInput } from "ink";
222
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef, useState as useState3 } from "react";
223
+ import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
179
224
  import { spawn } from "child_process";
180
- import {
181
- createReadStream,
182
- readFileSync,
183
- statSync,
184
- unlinkSync,
185
- watch,
186
- writeFileSync
187
- } from "fs";
188
- import { access, readFile as readFile2 } from "fs/promises";
225
+ import { createReadStream, watch } from "fs";
226
+ import { access, readFile as readFile4 } from "fs/promises";
189
227
  import { createServer } from "http";
190
228
  import { createRequire } from "module";
191
- import { dirname, extname, join as join3 } from "path";
229
+ import { dirname as dirname2, extname, join as join6 } from "path";
192
230
  import { fileURLToPath } from "url";
193
231
  import { WebSocketServer } from "ws";
194
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
232
+
233
+ // src/lib/config.ts
234
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
235
+ import { join as join4 } from "path";
236
+ async function loadConfig(outputDir) {
237
+ try {
238
+ const content = await readFile2(join4(outputDir, "config.json"), "utf-8");
239
+ return JSON.parse(content);
240
+ } catch {
241
+ return {};
242
+ }
243
+ }
244
+ async function saveConfig(outputDir, config) {
245
+ await mkdir2(outputDir, { recursive: true });
246
+ await writeFile2(join4(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
247
+ }
248
+
249
+ // src/ui/CredentialSetupUI.tsx
250
+ import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
251
+ import { Box as Box3, Text as Text3, useInput } from "ink";
252
+
253
+ // src/lib/credentials.ts
254
+ import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
255
+ import { homedir } from "os";
256
+ import { dirname, join as join5 } from "path";
257
+ var ENVIRONMENTS = ["go", "fmo", "staging", "integration", "test1"];
258
+ var GLOBAL_CREDENTIALS_DIR = join5(homedir(), ".kizenappbuilder");
259
+ var GLOBAL_CREDENTIALS_PATH = join5(GLOBAL_CREDENTIALS_DIR, "credentials.json");
260
+ function isValidEnvironment(value) {
261
+ return ENVIRONMENTS.includes(value);
262
+ }
263
+ function parseCredentials(raw) {
264
+ if (typeof raw !== "object" || raw === null) {
265
+ throw new Error("Credentials must be a JSON object");
266
+ }
267
+ const obj = raw;
268
+ const env = obj.environment;
269
+ return {
270
+ apiKey: typeof obj.apiKey === "string" ? obj.apiKey : "",
271
+ userId: typeof obj.userId === "string" ? obj.userId : "",
272
+ businessId: typeof obj.businessId === "string" ? obj.businessId : "",
273
+ environment: isValidEnvironment(env) ? env : "go"
274
+ };
275
+ }
276
+ async function loadCredentialsFromFile(filePath) {
277
+ const content = await readFile3(filePath, "utf-8");
278
+ return parseCredentials(JSON.parse(content));
279
+ }
280
+ async function loadGlobalCredentials() {
281
+ try {
282
+ return await loadCredentialsFromFile(GLOBAL_CREDENTIALS_PATH);
283
+ } catch {
284
+ return null;
285
+ }
286
+ }
287
+ async function saveGlobalCredentials(credentials) {
288
+ await mkdir3(dirname(GLOBAL_CREDENTIALS_PATH), { recursive: true });
289
+ await writeFile3(GLOBAL_CREDENTIALS_PATH, JSON.stringify(credentials, null, 2), "utf-8");
290
+ }
291
+
292
+ // src/ui/CredentialSetupUI.tsx
293
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
294
+ var FIELDS = ["apiKey", "userId", "businessId"];
295
+ var FIELD_LABELS = {
296
+ apiKey: "API Key",
297
+ userId: "User ID",
298
+ businessId: "Business ID"
299
+ };
300
+ var Hint = ({ text }) => /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: text });
301
+ var CredentialSetupUI = ({
302
+ initialMode,
303
+ onComplete,
304
+ onCancel
305
+ }) => {
306
+ const [phase, setPhase] = useState2(
307
+ initialMode === "global" ? { type: "loading" } : { type: "mode-select", cursor: 0 }
308
+ );
309
+ const [inputBuffer, setInputBuffer] = useState2("");
310
+ const [error, setError] = useState2(null);
311
+ const handleModeChosen = useCallback(
312
+ async (mode) => {
313
+ if (mode === "local") {
314
+ onComplete({ mode: "local", credentials: null });
315
+ return;
316
+ }
317
+ setPhase({ type: "loading" });
318
+ const existing = await loadGlobalCredentials();
319
+ const envCursor2 = existing ? Math.max(0, ENVIRONMENTS.indexOf(existing.environment)) : 0;
320
+ setPhase({
321
+ type: "creds-entry",
322
+ field: 0,
323
+ values: existing ?? {},
324
+ envCursor: envCursor2
325
+ });
326
+ },
327
+ [onComplete]
328
+ );
329
+ const handleSave = useCallback(
330
+ async (values2, envCursor2) => {
331
+ const environment = ENVIRONMENTS[envCursor2] ?? "go";
332
+ const credentials = {
333
+ apiKey: values2.apiKey ?? "",
334
+ userId: values2.userId ?? "",
335
+ businessId: values2.businessId ?? "",
336
+ environment
337
+ };
338
+ setPhase({ type: "saving" });
339
+ try {
340
+ await saveGlobalCredentials(credentials);
341
+ onComplete({ mode: "global", credentials });
342
+ } catch (err) {
343
+ setError(err instanceof Error ? err.message : String(err));
344
+ setPhase({ type: "creds-entry", field: 0, values: values2, envCursor: envCursor2 });
345
+ }
346
+ },
347
+ [onComplete]
348
+ );
349
+ useInput((input, key) => {
350
+ if (key.ctrl && input === "c") {
351
+ process.exit(0);
352
+ }
353
+ if (key.escape) {
354
+ onCancel?.();
355
+ return;
356
+ }
357
+ if (phase.type === "mode-select") {
358
+ if (key.upArrow) {
359
+ setPhase({ type: "mode-select", cursor: 0 });
360
+ } else if (key.downArrow) {
361
+ setPhase({ type: "mode-select", cursor: 1 });
362
+ } else if (key.return) {
363
+ const mode = phase.cursor === 0 ? "global" : "local";
364
+ void handleModeChosen(mode);
365
+ }
366
+ return;
367
+ }
368
+ if (phase.type === "creds-entry") {
369
+ const { field, values: values2, envCursor: envCursor2 } = phase;
370
+ const isEnvField2 = field === FIELDS.length;
371
+ if (isEnvField2) {
372
+ if (key.leftArrow) {
373
+ setPhase({
374
+ ...phase,
375
+ envCursor: (envCursor2 - 1 + ENVIRONMENTS.length) % ENVIRONMENTS.length
376
+ });
377
+ } else if (key.rightArrow) {
378
+ setPhase({ ...phase, envCursor: (envCursor2 + 1) % ENVIRONMENTS.length });
379
+ } else if (key.upArrow) {
380
+ setPhase({ ...phase, field: field - 1 });
381
+ setInputBuffer(values2[FIELDS[field - 1]] ?? "");
382
+ } else if (key.return) {
383
+ void handleSave(values2, envCursor2);
384
+ }
385
+ return;
386
+ }
387
+ const fieldName = FIELDS[field];
388
+ if (!fieldName) {
389
+ return;
390
+ }
391
+ if (key.backspace || key.delete) {
392
+ setInputBuffer((prev) => prev.slice(0, -1));
393
+ } else if (key.upArrow && field > 0) {
394
+ const updatedValues = { ...values2, [fieldName]: inputBuffer };
395
+ const prevField = field - 1;
396
+ setPhase({ ...phase, field: prevField, values: updatedValues });
397
+ setInputBuffer(updatedValues[FIELDS[prevField]] ?? "");
398
+ } else if (key.return || key.tab || key.downArrow) {
399
+ const updatedValues = { ...values2, [fieldName]: inputBuffer };
400
+ const nextField = field + 1;
401
+ setPhase({ ...phase, field: nextField, values: updatedValues });
402
+ if (nextField < FIELDS.length) {
403
+ setInputBuffer(updatedValues[FIELDS[nextField]] ?? "");
404
+ } else {
405
+ setInputBuffer("");
406
+ }
407
+ } else if (input && !key.ctrl && !key.meta) {
408
+ setInputBuffer((prev) => prev + input);
409
+ }
410
+ }
411
+ });
412
+ useEffect2(() => {
413
+ if (initialMode === "global") {
414
+ void handleModeChosen("global");
415
+ }
416
+ }, []);
417
+ useEffect2(() => {
418
+ if (phase.type === "creds-entry" && phase.field === 0) {
419
+ setInputBuffer(phase.values.apiKey ?? "");
420
+ }
421
+ }, [phase.type]);
422
+ if (phase.type === "loading" || phase.type === "saving") {
423
+ return /* @__PURE__ */ jsx3(Box3, { paddingY: 1, paddingX: 2, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: phase.type === "loading" ? "Loading credentials\u2026" : "Saving credentials\u2026" }) });
424
+ }
425
+ if (phase.type === "done") {
426
+ return null;
427
+ }
428
+ if (phase.type === "mode-select") {
429
+ const options = [
430
+ { label: "Global", desc: `~/.kizenappbuilder/credentials.json`, mode: "global" },
431
+ { label: "Local", desc: "Enter credentials in the browser dev tools", mode: "local" }
432
+ ];
433
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
434
+ /* @__PURE__ */ jsx3(Logo, {}),
435
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, children: [
436
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "Kizen App Builder" }),
437
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(24) })
438
+ ] }),
439
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
440
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Credential mode" }),
441
+ options.map((opt, i) => {
442
+ const selected = phase.cursor === i;
443
+ return /* @__PURE__ */ jsxs2(Box3, { gap: 2, children: [
444
+ /* @__PURE__ */ jsx3(Text3, { ...selected && { color: "cyan" }, children: selected ? "\u276F" : " " }),
445
+ /* @__PURE__ */ jsx3(Text3, { bold: selected, ...selected && { color: "cyan" }, children: opt.label }),
446
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: opt.desc })
447
+ ] }, opt.mode);
448
+ })
449
+ ] }),
450
+ /* @__PURE__ */ jsx3(Hint, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit" })
451
+ ] });
452
+ }
453
+ const { field: activeField, values, envCursor } = phase;
454
+ const isEnvField = activeField === FIELDS.length;
455
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
456
+ /* @__PURE__ */ jsx3(Logo, {}),
457
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, children: [
458
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "Kizen App Builder" }),
459
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(24) })
460
+ ] }),
461
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
462
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Global credentials" }),
463
+ /* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
464
+ "Saved to: ",
465
+ GLOBAL_CREDENTIALS_PATH
466
+ ] })
467
+ ] }),
468
+ error && /* @__PURE__ */ jsxs2(Text3, { color: "red", children: [
469
+ "Error: ",
470
+ error
471
+ ] }),
472
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
473
+ FIELDS.map((name, i) => {
474
+ const isActive = activeField === i;
475
+ const displayValue = isActive ? inputBuffer : values[name] ?? "";
476
+ return /* @__PURE__ */ jsxs2(Box3, { gap: 2, children: [
477
+ /* @__PURE__ */ jsx3(Box3, { width: 12, children: /* @__PURE__ */ jsx3(Text3, { bold: isActive, ...isActive && { color: "cyan" }, children: FIELD_LABELS[name] }) }),
478
+ /* @__PURE__ */ jsx3(Text3, { ...isActive && { color: "cyan" }, children: ">" }),
479
+ /* @__PURE__ */ jsx3(Text3, { children: displayValue }),
480
+ isActive && /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "\u2588" })
481
+ ] }, name);
482
+ }),
483
+ /* @__PURE__ */ jsxs2(Box3, { gap: 2, children: [
484
+ /* @__PURE__ */ jsx3(Box3, { width: 12, children: /* @__PURE__ */ jsx3(Text3, { bold: isEnvField, ...isEnvField && { color: "cyan" }, children: "Environment" }) }),
485
+ /* @__PURE__ */ jsx3(Text3, { ...isEnvField && { color: "cyan" }, children: ">" }),
486
+ /* @__PURE__ */ jsx3(Box3, { gap: 1, children: ENVIRONMENTS.map((env, i) => {
487
+ const isSelected = i === envCursor;
488
+ if (isEnvField) {
489
+ return /* @__PURE__ */ jsx3(Text3, { bold: isSelected, ...isSelected && { color: "cyan" }, children: isSelected ? `[${env}]` : env }, env);
490
+ }
491
+ return /* @__PURE__ */ jsx3(Text3, { dimColor: !isSelected, bold: isSelected, children: env }, env);
492
+ }) })
493
+ ] })
494
+ ] }),
495
+ isEnvField ? /* @__PURE__ */ jsx3(Hint, { text: "\u2190\u2192 to select \xB7 \u2191\u2193 to move \xB7 Enter to save \xB7 Esc to cancel" }) : /* @__PURE__ */ jsx3(Hint, { text: "\u2191\u2193 to move \xB7 Backspace to delete \xB7 Esc to cancel" })
496
+ ] });
497
+ };
498
+
499
+ // src/ui/DevUI.tsx
500
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
195
501
  var SKIP_WATCH_PREFIXES = [".kizenapp", ".git"];
196
502
  var LOG_LIMIT = 50;
197
503
  var LOG_DISPLAY = 8;
@@ -212,8 +518,8 @@ var MIME_TYPES = {
212
518
  };
213
519
  var SPINNER_FRAMES2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
214
520
  var Spinner2 = () => {
215
- const [frame, setFrame] = useState2(0);
216
- useEffect2(() => {
521
+ const [frame, setFrame] = useState3(0);
522
+ useEffect3(() => {
217
523
  const id = setInterval(() => {
218
524
  setFrame((prev) => (prev + 1) % SPINNER_FRAMES2.length);
219
525
  }, 80);
@@ -221,15 +527,15 @@ var Spinner2 = () => {
221
527
  clearInterval(id);
222
528
  };
223
529
  }, []);
224
- return /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: SPINNER_FRAMES2[frame] ?? "\u280B" });
530
+ return /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: SPINNER_FRAMES2[frame] ?? "\u280B" });
225
531
  };
226
532
  function getViewerPath() {
227
533
  const filename = fileURLToPath(import.meta.url);
228
- return join3(dirname(filename), "viewer");
534
+ return join6(dirname2(filename), "viewer");
229
535
  }
230
536
  function getElectronMainPath() {
231
537
  const filename = fileURLToPath(import.meta.url);
232
- return join3(dirname(filename), "electron", "main.js");
538
+ return join6(dirname2(filename), "electron", "main.js");
233
539
  }
234
540
  async function fileExists(filePath) {
235
541
  try {
@@ -239,83 +545,114 @@ async function fileExists(filePath) {
239
545
  return false;
240
546
  }
241
547
  }
242
- function createRequestHandler(viewerPath, createServerLog, createProxyLog) {
548
+ function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef) {
243
549
  return (req, res) => {
244
550
  void (async () => {
245
551
  const url = req.url ?? "/";
246
- createServerLog(`Received request: ${url}`);
247
- if (url === "/api/bundle") {
248
- const bundlePath = join3(process.cwd(), ".kizenapp", "bundle.json");
249
- try {
250
- const content = await readFile2(bundlePath, "utf-8");
251
- res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
252
- res.end(content);
253
- } catch {
552
+ try {
553
+ createServerLog(`Received request: ${url}`);
554
+ if (url === "/api/credentials") {
254
555
  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
255
- res.end("{}");
556
+ res.end(credentialsRef.current !== null ? JSON.stringify(credentialsRef.current) : "{}");
557
+ return;
256
558
  }
257
- return;
258
- }
259
- if (url.startsWith("/api/proxy")) {
260
- const proxyTarget = req.headers["x-proxy-target"];
261
- if (typeof proxyTarget !== "string") {
262
- res.writeHead(400);
263
- res.end("Missing x-proxy-target header");
559
+ if (url === "/api/bundle") {
560
+ const bundlePath = join6(process.cwd(), ".kizenapp", "bundle.json");
561
+ try {
562
+ const content = await readFile4(bundlePath, "utf-8");
563
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
564
+ res.end(content);
565
+ } catch {
566
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
567
+ res.end("{}");
568
+ }
569
+ return;
570
+ }
571
+ if (url.startsWith("/api/proxy")) {
572
+ const proxyTarget = req.headers["x-proxy-target"];
573
+ if (typeof proxyTarget !== "string") {
574
+ res.writeHead(400);
575
+ res.end("Missing x-proxy-target header");
576
+ return;
577
+ }
578
+ const upstreamPath = url.slice("/api/proxy".length) || "/";
579
+ const upstreamUrl = `${proxyTarget}${upstreamPath}`;
580
+ const chunks = [];
581
+ for await (const chunk of req) {
582
+ chunks.push(chunk);
583
+ }
584
+ const body = chunks.length > 0 ? Buffer.concat(chunks) : void 0;
585
+ const { host, "x-proxy-target": _drop, ...forwardHeaders } = req.headers;
586
+ const resolvedBody = body && body.length > 0 ? body : void 0;
587
+ const upstream = await fetch(upstreamUrl, {
588
+ ...req.method !== void 0 && { method: req.method },
589
+ headers: forwardHeaders,
590
+ ...resolvedBody !== void 0 && { body: resolvedBody }
591
+ });
592
+ createProxyLog(`${req.method ?? "GET"} ${upstreamPath} \u2192 ${String(upstream.status)}`);
593
+ const responseHeaders = Object.fromEntries(upstream.headers);
594
+ delete responseHeaders["content-encoding"];
595
+ delete responseHeaders["content-length"];
596
+ res.writeHead(upstream.status, responseHeaders);
597
+ res.end(Buffer.from(await upstream.arrayBuffer()));
264
598
  return;
265
599
  }
266
- const upstreamPath = url.slice("/api/proxy".length) || "/";
267
- const upstreamUrl = `${proxyTarget}${upstreamPath}`;
268
- const chunks = [];
269
- for await (const chunk of req) {
270
- chunks.push(chunk);
600
+ const rawPath = url === "/" ? "/index.html" : url;
601
+ const filePath = join6(viewerPath, rawPath);
602
+ const resolvedPath = await fileExists(filePath) ? filePath : join6(viewerPath, "index.html");
603
+ const ext = extname(resolvedPath);
604
+ const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
605
+ res.writeHead(200, { "Content-Type": mimeType });
606
+ createReadStream(resolvedPath).pipe(res);
607
+ } catch (err) {
608
+ createProxyLog(
609
+ `Error handling ${url}: ${err instanceof Error ? err.message : String(err)}`
610
+ );
611
+ if (!res.headersSent) {
612
+ res.writeHead(502);
613
+ res.end("Bad Gateway");
271
614
  }
272
- const body = chunks.length > 0 ? Buffer.concat(chunks) : void 0;
273
- const { host, "x-proxy-target": _drop, ...forwardHeaders } = req.headers;
274
- const resolvedBody = body && body.length > 0 ? body : void 0;
275
- const upstream = await fetch(upstreamUrl, {
276
- ...req.method !== void 0 && { method: req.method },
277
- headers: forwardHeaders,
278
- ...resolvedBody !== void 0 && { body: resolvedBody }
279
- });
280
- createProxyLog(`${req.method ?? "GET"} ${upstreamPath} \u2192 ${String(upstream.status)}`);
281
- const responseHeaders = Object.fromEntries(upstream.headers);
282
- delete responseHeaders["content-encoding"];
283
- delete responseHeaders["content-length"];
284
- res.writeHead(upstream.status, responseHeaders);
285
- res.end(Buffer.from(await upstream.arrayBuffer()));
286
- return;
287
615
  }
288
- const rawPath = url === "/" ? "/index.html" : url;
289
- const filePath = join3(viewerPath, rawPath);
290
- const resolvedPath = await fileExists(filePath) ? filePath : join3(viewerPath, "index.html");
291
- const ext = extname(resolvedPath);
292
- const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
293
- res.writeHead(200, { "Content-Type": mimeType });
294
- createReadStream(resolvedPath).pipe(res);
295
616
  })();
296
617
  };
297
618
  }
298
- var DevUI = ({ port, pluginDir, outputDir }) => {
299
- const [status, setStatus] = useState2("starting");
300
- const [errorMessage, setErrorMessage] = useState2(null);
301
- const [serverLogHistory, setServerLogHistory] = useState2([]);
302
- const [buildLogHistory, setBuildLogHistory] = useState2([]);
303
- const [proxyLogHistory, setProxyLogHistory] = useState2([]);
304
- const [buildStatus, setBuildStatus] = useState2("pending");
305
- const [buildError, setBuildError] = useState2(null);
306
- const [lastBuilt, setLastBuilt] = useState2(null);
307
- const [wsClientCount, setWsClientCount] = useState2(0);
619
+ var DevUI = ({
620
+ port,
621
+ pluginDir,
622
+ outputDir,
623
+ credentials: initialCredentials
624
+ }) => {
625
+ const credentialsRef = useRef(initialCredentials);
626
+ const [credMode, setCredMode] = useState3("main");
627
+ const [status, setStatus] = useState3("starting");
628
+ const [errorMessage, setErrorMessage] = useState3(null);
629
+ const [serverLogHistory, setServerLogHistory] = useState3([]);
630
+ const [buildLogHistory, setBuildLogHistory] = useState3([]);
631
+ const [proxyLogHistory, setProxyLogHistory] = useState3([]);
632
+ const [buildStatus, setBuildStatus] = useState3("pending");
633
+ const [buildError, setBuildError] = useState3(null);
634
+ const [lastBuilt, setLastBuilt] = useState3(null);
635
+ const [wsClientCount, setWsClientCount] = useState3(0);
308
636
  const buildingRef = useRef(false);
309
637
  const electronLaunchedRef = useRef(false);
310
638
  const debounceTimerRef = useRef(null);
311
639
  const wsClientsRef = useRef(/* @__PURE__ */ new Set());
312
640
  const pendingMessagesRef = useRef([]);
313
- useInput((input, key) => {
314
- if (input === "q" || key.ctrl && input === "c") {
315
- process.exit(0);
316
- }
317
- });
318
- useEffect2(() => {
641
+ useInput2(
642
+ (input, key) => {
643
+ if (input === "q" || key.ctrl && input === "c") {
644
+ process.exit(0);
645
+ }
646
+ if (input === "c") {
647
+ setCredMode("editing");
648
+ }
649
+ if (input === "d") {
650
+ broadcast({ type: "open-devtools" });
651
+ }
652
+ },
653
+ { isActive: credMode === "main" }
654
+ );
655
+ useEffect3(() => {
319
656
  if (status !== "running" || electronLaunchedRef.current) {
320
657
  return;
321
658
  }
@@ -323,27 +660,7 @@ var DevUI = ({ port, pluginDir, outputDir }) => {
323
660
  const _require = createRequire(import.meta.url);
324
661
  const electronBin = _require("electron");
325
662
  const electronMain = getElectronMainPath();
326
- const userDataDir = join3(outputDir, ".electron");
327
- if (process.platform === "darwin") {
328
- try {
329
- const plistPath = join3(dirname(dirname(electronBin)), "Info.plist");
330
- const plist = readFileSync(plistPath, "utf-8");
331
- if (!plist.includes(">Kizen Dev<")) {
332
- if (statSync(plistPath).nlink > 1) {
333
- unlinkSync(plistPath);
334
- writeFileSync(plistPath, plist);
335
- }
336
- writeFileSync(
337
- plistPath,
338
- plist.replace(/(<key>CFBundleName<\/key>\s*<string>)[^<]*(<\/string>)/, "$1Kizen Dev$2").replace(
339
- /(<key>CFBundleDisplayName<\/key>\s*<string>)[^<]*(<\/string>)/,
340
- "$1Kizen Dev$2"
341
- )
342
- );
343
- }
344
- } catch {
345
- }
346
- }
663
+ const userDataDir = join6(outputDir, ".electron");
347
664
  const proc = spawn(
348
665
  electronBin,
349
666
  [electronMain, `--port=${String(port)}`, `--user-data-dir=${userDataDir}`],
@@ -356,12 +673,12 @@ var DevUI = ({ port, pluginDir, outputDir }) => {
356
673
  proc.kill();
357
674
  });
358
675
  }, [status, port, outputDir]);
359
- const createServerLog = useCallback((message) => {
676
+ const createServerLog = useCallback2((message) => {
360
677
  setServerLogHistory(
361
678
  (h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-LOG_LIMIT)
362
679
  );
363
680
  }, []);
364
- const broadcast = useCallback((msg) => {
681
+ const broadcast = useCallback2((msg) => {
365
682
  const json = JSON.stringify(msg);
366
683
  if (wsClientsRef.current.size === 0) {
367
684
  pendingMessagesRef.current = [...pendingMessagesRef.current, json].slice(-LOG_LIMIT);
@@ -373,7 +690,16 @@ var DevUI = ({ port, pluginDir, outputDir }) => {
373
690
  }
374
691
  }
375
692
  }, []);
376
- const createProxyLog = useCallback(
693
+ const handleCredentialsDone = useCallback2(
694
+ (result) => {
695
+ credentialsRef.current = result.credentials;
696
+ setCredMode("main");
697
+ void saveConfig(outputDir, { credentialMode: result.mode });
698
+ broadcast({ type: "credentials-updated", credentials: result.credentials });
699
+ },
700
+ [outputDir, broadcast]
701
+ );
702
+ const createProxyLog = useCallback2(
377
703
  (message) => {
378
704
  setProxyLogHistory(
379
705
  (h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-LOG_LIMIT)
@@ -382,7 +708,7 @@ var DevUI = ({ port, pluginDir, outputDir }) => {
382
708
  },
383
709
  [broadcast]
384
710
  );
385
- const createBuildLog = useCallback(
711
+ const createBuildLog = useCallback2(
386
712
  (message) => {
387
713
  setBuildLogHistory(
388
714
  (h) => [...h, `${(/* @__PURE__ */ new Date()).toLocaleTimeString()}: ${message}`].slice(-LOG_LIMIT)
@@ -391,7 +717,7 @@ var DevUI = ({ port, pluginDir, outputDir }) => {
391
717
  },
392
718
  [broadcast]
393
719
  );
394
- const triggerBuild = useCallback(() => {
720
+ const triggerBuild = useCallback2(() => {
395
721
  if (buildingRef.current) {
396
722
  return;
397
723
  }
@@ -417,7 +743,7 @@ var DevUI = ({ port, pluginDir, outputDir }) => {
417
743
  buildingRef.current = false;
418
744
  });
419
745
  }, [pluginDir, outputDir, createBuildLog]);
420
- useEffect2(() => {
746
+ useEffect3(() => {
421
747
  triggerBuild();
422
748
  const watcher = watch(pluginDir, { recursive: true }, (_, filename) => {
423
749
  if (!filename) {
@@ -440,15 +766,20 @@ var DevUI = ({ port, pluginDir, outputDir }) => {
440
766
  }
441
767
  };
442
768
  }, [pluginDir, triggerBuild, createBuildLog]);
443
- useEffect2(() => {
769
+ useEffect3(() => {
444
770
  const viewerPath = getViewerPath();
445
- void fileExists(join3(viewerPath, "index.html")).then((viewerBuilt) => {
771
+ void fileExists(join6(viewerPath, "index.html")).then((viewerBuilt) => {
446
772
  if (!viewerBuilt) {
447
773
  setErrorMessage("Viewer not built. Run 'pnpm build:viewer' first.");
448
774
  setStatus("error");
449
775
  return;
450
776
  }
451
- const handler = createRequestHandler(viewerPath, createServerLog, createProxyLog);
777
+ const handler = createRequestHandler(
778
+ viewerPath,
779
+ createServerLog,
780
+ createProxyLog,
781
+ credentialsRef
782
+ );
452
783
  const server = createServer(handler);
453
784
  const wss = new WebSocketServer({ server });
454
785
  wss.on("connection", (ws) => {
@@ -480,95 +811,173 @@ var DevUI = ({ port, pluginDir, outputDir }) => {
480
811
  };
481
812
  });
482
813
  }, [port, createServerLog, createProxyLog]);
814
+ if (credMode === "editing") {
815
+ return /* @__PURE__ */ jsx4(
816
+ CredentialSetupUI,
817
+ {
818
+ onComplete: handleCredentialsDone,
819
+ onCancel: () => {
820
+ setCredMode("main");
821
+ }
822
+ }
823
+ );
824
+ }
483
825
  const elapsedSeconds = lastBuilt !== null ? Math.round((Date.now() - lastBuilt.getTime()) / 1e3) : null;
484
826
  const elapsedLabel = elapsedSeconds === null ? "" : elapsedSeconds < 5 ? " (just now)" : ` (${String(elapsedSeconds)}s ago)`;
485
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingY: 1, paddingX: 1, children: [
486
- /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx2(Box2, { gap: 1, children: /* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "Kizen App Builder" }) }) }),
487
- /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
488
- /* @__PURE__ */ jsxs2(Box2, { gap: 1, marginBottom: 0, children: [
489
- buildStatus === "pending" && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Build waiting..." }),
490
- buildStatus === "building" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
491
- /* @__PURE__ */ jsx2(Spinner2, {}),
492
- /* @__PURE__ */ jsx2(Text2, { children: "Building Plugin Package..." })
827
+ return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", paddingY: 1, paddingX: 2, children: [
828
+ /* @__PURE__ */ jsx4(Logo, {}),
829
+ /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
830
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "cyan", children: "Kizen App Builder" }),
831
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "\u2500".repeat(24) })
832
+ ] }),
833
+ /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", children: [
834
+ /* @__PURE__ */ jsxs3(Box4, { gap: 2, marginBottom: 0, children: [
835
+ /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "Build" }),
836
+ buildStatus === "pending" && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "waiting\u2026" }),
837
+ buildStatus === "building" && /* @__PURE__ */ jsxs3(Box4, { gap: 1, children: [
838
+ /* @__PURE__ */ jsx4(Spinner2, {}),
839
+ /* @__PURE__ */ jsx4(Text4, { children: "Building plugin package\u2026" })
493
840
  ] }),
494
- buildStatus === "done" && /* @__PURE__ */ jsxs2(Text2, { color: "green", children: [
495
- "\u2713 Built Plugin Package",
496
- lastBuilt !== null ? ` at ${lastBuilt.toLocaleTimeString()}` : "",
497
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: elapsedLabel })
841
+ buildStatus === "done" && /* @__PURE__ */ jsxs3(Box4, { gap: 1, children: [
842
+ /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u2713 Built" }),
843
+ lastBuilt !== null && /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
844
+ "at ",
845
+ lastBuilt.toLocaleTimeString(),
846
+ elapsedLabel
847
+ ] })
498
848
  ] }),
499
- buildStatus === "error" && /* @__PURE__ */ jsxs2(Text2, { color: "red", children: [
500
- "\u2717 Build error: ",
849
+ buildStatus === "error" && /* @__PURE__ */ jsxs3(Text4, { color: "red", children: [
850
+ "\u2717 ",
501
851
  buildError ?? "unknown error"
502
852
  ] })
503
853
  ] }),
504
- /* @__PURE__ */ jsx2(
505
- Box2,
854
+ /* @__PURE__ */ jsx4(
855
+ Box4,
506
856
  {
507
857
  flexDirection: "column",
508
858
  height: LOG_DISPLAY,
509
- borderStyle: "round",
859
+ borderStyle: "single",
510
860
  borderColor: "gray",
511
861
  overflow: "hidden",
512
- children: buildLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate", children: log }, index))
862
+ children: buildLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx4(Text4, { dimColor: true, wrap: "truncate", children: log }, index))
513
863
  }
514
864
  )
515
865
  ] }),
516
- /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
517
- /* @__PURE__ */ jsxs2(Box2, { gap: 2, children: [
518
- status === "starting" && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Starting server..." }),
519
- status === "running" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
520
- /* @__PURE__ */ jsx2(Text2, { color: "green", children: "\u2713 Dev Server running" }),
521
- /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
866
+ /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
867
+ /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
868
+ /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "Server" }),
869
+ status === "starting" && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "starting\u2026" }),
870
+ status === "running" && /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
871
+ /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u2713 Running" }),
872
+ /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
522
873
  "\u25CF ",
523
874
  wsClientCount,
524
875
  " viewer",
525
876
  wsClientCount !== 1 ? "s" : ""
526
877
  ] })
527
878
  ] }),
528
- status === "error" && /* @__PURE__ */ jsxs2(Text2, { color: "red", children: [
879
+ status === "error" && /* @__PURE__ */ jsxs3(Text4, { color: "red", children: [
529
880
  "\u2717 ",
530
881
  errorMessage ?? "Server error"
531
882
  ] })
532
883
  ] }),
533
- /* @__PURE__ */ jsx2(
534
- Box2,
884
+ /* @__PURE__ */ jsx4(
885
+ Box4,
535
886
  {
536
887
  flexDirection: "column",
537
888
  height: LOG_DISPLAY,
538
- borderStyle: "round",
889
+ borderStyle: "single",
539
890
  borderColor: "gray",
540
891
  overflow: "hidden",
541
- children: serverLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate", children: log }, index))
892
+ children: serverLogHistory.slice(-LOG_DISPLAY).map((log, index) => /* @__PURE__ */ jsx4(Text4, { dimColor: true, wrap: "truncate", children: log }, index))
542
893
  }
543
894
  )
544
895
  ] }),
545
- /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
546
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Proxy" }),
547
- /* @__PURE__ */ jsx2(
548
- Box2,
896
+ /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, flexDirection: "column", children: [
897
+ /* @__PURE__ */ jsxs3(Box4, { gap: 2, children: [
898
+ /* @__PURE__ */ jsx4(Text4, { bold: true, dimColor: true, children: "Proxy" }),
899
+ /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u2713 Active" })
900
+ ] }),
901
+ /* @__PURE__ */ jsx4(
902
+ Box4,
549
903
  {
550
904
  flexDirection: "column",
551
905
  height: LOG_DISPLAY,
552
- borderStyle: "round",
906
+ borderStyle: "single",
553
907
  borderColor: "gray",
554
908
  overflow: "hidden",
555
- children: proxyLogHistory.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " No proxy requests yet." }) : proxyLogHistory.slice(-LOG_DISPLAY).map((log, i) => /* @__PURE__ */ jsx2(Text2, { dimColor: true, wrap: "truncate", children: log }, i))
909
+ children: proxyLogHistory.length === 0 ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " No requests yet." }) : proxyLogHistory.slice(-LOG_DISPLAY).map((log, i) => /* @__PURE__ */ jsx4(Text4, { dimColor: true, wrap: "truncate", children: log }, i))
556
910
  }
557
911
  )
558
912
  ] }),
559
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "q to quit" }) })
913
+ /* @__PURE__ */ jsxs3(Box4, { marginTop: 1, gap: 1, children: [
914
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "[" }),
915
+ /* @__PURE__ */ jsx4(Text4, { children: "q" }),
916
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "] quit [" }),
917
+ /* @__PURE__ */ jsx4(Text4, { children: "c" }),
918
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "] credentials [" }),
919
+ /* @__PURE__ */ jsx4(Text4, { children: "d" }),
920
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "] devtools" })
921
+ ] })
560
922
  ] });
561
923
  };
562
924
 
563
925
  // src/commands/dev.ts
926
+ async function runSetupUI(initialMode) {
927
+ return new Promise((resolve) => {
928
+ let unmountFn = null;
929
+ const handleComplete = (result) => {
930
+ unmountFn?.();
931
+ resolve(result);
932
+ };
933
+ const { unmount } = render2(
934
+ createElement2(CredentialSetupUI, {
935
+ ...initialMode !== void 0 && { initialMode },
936
+ onComplete: handleComplete
937
+ }),
938
+ { exitOnCtrlC: false }
939
+ );
940
+ unmountFn = unmount;
941
+ });
942
+ }
564
943
  function devCommand(program2) {
565
- program2.command("dev").description("Start the plugin viewer dev server").option("-p, --port <port>", "port to listen on", "3121").action(async (options) => {
944
+ 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").action(async (options) => {
566
945
  const port = parseInt(options.port, 10);
567
946
  const pluginDir = process.cwd();
568
947
  const outputDir = `${pluginDir}/.kizenapp`;
569
- const { waitUntilExit } = render2(createElement2(DevUI, { port, pluginDir, outputDir }), {
570
- exitOnCtrlC: false
571
- });
948
+ ensureGitignore(pluginDir);
949
+ let credentials = null;
950
+ let credentialMode;
951
+ if (options.credentials) {
952
+ credentials = await loadCredentialsFromFile(options.credentials);
953
+ } else {
954
+ const config = await loadConfig(outputDir);
955
+ if (config.credentialMode === "local") {
956
+ credentialMode = "local";
957
+ } else if (config.credentialMode === "global") {
958
+ credentialMode = "global";
959
+ credentials = await loadGlobalCredentials();
960
+ if (!credentials) {
961
+ const result = await runSetupUI("global");
962
+ credentials = result.credentials;
963
+ }
964
+ } else {
965
+ const result = await runSetupUI();
966
+ credentials = result.credentials;
967
+ credentialMode = result.mode;
968
+ await saveConfig(outputDir, { credentialMode: result.mode });
969
+ }
970
+ }
971
+ const { waitUntilExit } = render2(
972
+ createElement2(DevUI, {
973
+ port,
974
+ pluginDir,
975
+ outputDir,
976
+ credentials,
977
+ ...credentialMode !== void 0 && { credentialMode }
978
+ }),
979
+ { exitOnCtrlC: false }
980
+ );
572
981
  await waitUntilExit();
573
982
  });
574
983
  }