@kizenapps/cli 0.7.0 → 0.8.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 (42) hide show
  1. package/dist/electron/main.js +9 -8
  2. package/dist/electron/main.js.map +1 -1
  3. package/dist/index.js +293 -58
  4. package/dist/index.js.map +1 -1
  5. package/dist/viewer/assets/index-B09qenTV.js +17 -0
  6. package/dist/viewer/assets/{index-C_6izRRh.js → index-CVd4BLit.js} +2 -2
  7. package/dist/viewer/assets/{index-CBBwr2o_.js → index-C_gRcfsL.js} +2 -2
  8. package/dist/viewer/assets/index-CcvbGL5I.js +17 -0
  9. package/dist/viewer/assets/index-Ci5up9QD.js +17 -0
  10. package/dist/viewer/assets/index-DOiSISo1.css +2 -0
  11. package/dist/viewer/assets/{index-DcGGXuA3.js → index-DSJAwx9Y.js} +2 -2
  12. package/dist/viewer/assets/index-WnCF0zx3.js +17 -0
  13. package/dist/viewer/assets/{index--m1PKX6N.js → index-_K33TupM.js} +2 -2
  14. package/dist/viewer/assets/index-dPV4go8k.js +17 -0
  15. package/dist/viewer/assets/index-xFuxlPJA.js +17 -0
  16. package/dist/viewer/index.html +2 -2
  17. package/package.json +1 -1
  18. package/dist/viewer/assets/index-2OcBnmZB.js +0 -17
  19. package/dist/viewer/assets/index-7ah1RuPy.js +0 -17
  20. package/dist/viewer/assets/index-B3gNsKTt.js +0 -17
  21. package/dist/viewer/assets/index-B40AtXqk.js +0 -17
  22. package/dist/viewer/assets/index-B4ikC-wc.js +0 -17
  23. package/dist/viewer/assets/index-BKOfPpLS.js +0 -17
  24. package/dist/viewer/assets/index-BM3JrC-1.js +0 -17
  25. package/dist/viewer/assets/index-BUnK11-F.js +0 -17
  26. package/dist/viewer/assets/index-Bc7IZVr6.js +0 -17
  27. package/dist/viewer/assets/index-C1KzcpVj.js +0 -17
  28. package/dist/viewer/assets/index-CHtXWYIY.js +0 -17
  29. package/dist/viewer/assets/index-CN9NvJkQ.js +0 -17
  30. package/dist/viewer/assets/index-CNKPBxhv.js +0 -17
  31. package/dist/viewer/assets/index-CgzK6zig.js +0 -17
  32. package/dist/viewer/assets/index-Cw2cxYy2.js +0 -17
  33. package/dist/viewer/assets/index-D06uhGtQ.js +0 -17
  34. package/dist/viewer/assets/index-DAHWT-g0.js +0 -17
  35. package/dist/viewer/assets/index-DAxJgHat.js +0 -17
  36. package/dist/viewer/assets/index-DF_geUaW.js +0 -17
  37. package/dist/viewer/assets/index-DlVmY-nR.js +0 -17
  38. package/dist/viewer/assets/index-FZw9Ev_R.js +0 -17
  39. package/dist/viewer/assets/index-M1Obm_99.js +0 -17
  40. package/dist/viewer/assets/index-_kM1Ri7v.js +0 -17
  41. package/dist/viewer/assets/index-aTt_sTlu.js +0 -17
  42. package/dist/viewer/assets/index-vYFGGFc2.js +0 -17
@@ -1,5 +1,5 @@
1
1
  // src/electron/main.ts
2
- import { app, BrowserWindow, session, nativeImage, Menu, ipcMain } from "electron";
2
+ import { app, BrowserWindow, session, nativeImage, Menu, ipcMain, screen } from "electron";
3
3
  import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  var args = process.argv.slice(2);
@@ -79,9 +79,10 @@ void app.whenReady().then(() => {
79
79
  if (process.platform === "darwin" && !icon.isEmpty()) {
80
80
  app.dock?.setIcon(icon);
81
81
  }
82
+ const { width, height } = screen.getPrimaryDisplay().workAreaSize;
82
83
  const win = new BrowserWindow({
83
- width: 1920,
84
- height: 1080,
84
+ width,
85
+ height,
85
86
  title: "Kizen App Builder",
86
87
  icon,
87
88
  webPreferences: {
@@ -186,11 +187,11 @@ void app.whenReady().then(() => {
186
187
  return;
187
188
  }
188
189
  const level = type === "warning" ? "warn" : ["log", "warn", "error", "info"].includes(type) ? type : "log";
189
- void Promise.all(args2.map((arg) => serializeArgAsync(arg, sessionId))).then(
190
- (serializedArgs) => {
191
- win.webContents.send("console-message", { level, args: serializedArgs });
192
- }
193
- );
190
+ void Promise.all(
191
+ args2.map((arg) => serializeArgAsync(arg, sessionId))
192
+ ).then((serializedArgs) => {
193
+ win.webContents.send("console-message", { level, args: serializedArgs });
194
+ });
194
195
  });
195
196
  void win.loadURL(`http://localhost:${String(port)}`);
196
197
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/electron/main.ts"],"sourcesContent":["import { app, BrowserWindow, session, nativeImage, Menu, ipcMain } from 'electron';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst args = process.argv.slice(2);\nconst port = parseInt(args.find((a) => a.startsWith('--port='))?.slice(7) ?? '3121', 10);\nconst userDataDir = args.find((a) => a.startsWith('--user-data-dir='))?.slice(16);\n\nif (userDataDir) {\n app.setPath('userData', userDataDir);\n}\n\napp.setName('Kizen App Builder');\n\nconst CSP_HEADERS = new Set([\n 'content-security-policy',\n 'content-security-policy-report-only',\n 'x-frame-options',\n]);\n\nconst KIZEN_DOMAINS = ['kizen.dev', 'kizen.com'];\n\nfunction isKizenUrl(url: string): boolean {\n try {\n const { hostname } = new URL(url);\n return KIZEN_DOMAINS.some((d) => hostname === d || hostname.endsWith('.' + d));\n } catch {\n return false;\n }\n}\n\nfunction setupSession(): void {\n // Strip CSP headers and fix SameSite cookies on Kizen responses.\n session.defaultSession.webRequest.onHeadersReceived((details, callback) => {\n const headers: Record<string, string[]> = {};\n\n for (const [key, value] of Object.entries(details.responseHeaders ?? {})) {\n if (!CSP_HEADERS.has(key.toLowerCase())) {\n headers[key] = value;\n }\n }\n\n if (isKizenUrl(details.url) && headers['set-cookie']) {\n headers['set-cookie'] = headers['set-cookie'].map((cookie) => {\n if (!/SameSite=/i.test(cookie)) {\n return cookie + '; SameSite=None; Secure';\n }\n\n return cookie.replace(/SameSite=\\w+/i, 'SameSite=None');\n });\n }\n\n callback({ responseHeaders: headers });\n });\n\n // Periodically re-patch existing Kizen cookies that arrived without SameSite=None.\n // Needed for cookies set before the interceptor was active or via JS document.cookie.\n setInterval(() => {\n void (async () => {\n for (const domain of KIZEN_DOMAINS) {\n try {\n const cookies = await session.defaultSession.cookies.get({ domain });\n for (const cookie of cookies) {\n if (cookie.sameSite !== 'no_restriction') {\n const raw = cookie.domain ?? domain;\n const bare = raw.startsWith('.') ? raw.slice(1) : raw;\n await session.defaultSession.cookies.set({\n url: `https://${bare}`,\n name: cookie.name,\n value: cookie.value,\n domain: raw,\n path: cookie.path ?? '/',\n secure: true,\n ...(cookie.httpOnly !== undefined && { httpOnly: cookie.httpOnly }),\n ...(cookie.expirationDate !== undefined && {\n expirationDate: cookie.expirationDate,\n }),\n sameSite: 'no_restriction',\n });\n }\n }\n } catch {\n /* ignore */\n }\n }\n })();\n }, 2000);\n}\n\nvoid app.whenReady().then(() => {\n Menu.setApplicationMenu(null);\n\n setupSession();\n\n const iconPath = join(dirname(fileURLToPath(import.meta.url)), 'icon.png');\n const icon = nativeImage.createFromPath(iconPath);\n\n if (process.platform === 'darwin' && !icon.isEmpty()) {\n app.dock?.setIcon(icon);\n }\n\n const win = new BrowserWindow({\n width: 1920,\n height: 1080,\n title: 'Kizen App Builder',\n icon,\n webPreferences: {\n nodeIntegration: false,\n contextIsolation: true,\n sandbox: false,\n preload: join(dirname(fileURLToPath(import.meta.url)), 'preload.cjs'),\n },\n });\n\n ipcMain.on('open-devtools', () => {\n win.webContents.openDevTools();\n });\n\n interface PropertyPreview {\n name: string;\n type: string;\n value?: string;\n subtype?: string;\n valuePreview?: ObjectPreview;\n }\n interface ObjectPreview {\n type: string;\n subtype?: string;\n overflow: boolean;\n properties: PropertyPreview[];\n }\n interface RemoteObject {\n type: string;\n subtype?: string;\n value?: unknown;\n description?: string;\n preview?: ObjectPreview;\n objectId?: string;\n }\n interface ConsoleApiCalledParams {\n type: string;\n args: RemoteObject[];\n }\n\n function parseProp(prop: PropertyPreview): unknown {\n if (prop.type === 'number') {\n return Number(prop.value);\n }\n if (prop.type === 'boolean') {\n return prop.value === 'true';\n }\n if (prop.type === 'undefined') {\n return undefined;\n }\n if (prop.type === 'object' && prop.value === 'null') {\n return null;\n }\n if (prop.type === 'object' && prop.valuePreview) {\n const { valuePreview } = prop;\n if (valuePreview.subtype === 'array') {\n return valuePreview.properties\n .filter((p) => /^\\d+$/.test(p.name))\n .sort((a, b) => parseInt(a.name) - parseInt(b.name))\n .map(parseProp);\n }\n const obj: Record<string, unknown> = {};\n for (const p of valuePreview.properties) {\n obj[p.name] = parseProp(p);\n }\n return obj;\n }\n return prop.value ?? `[${prop.type}]`;\n }\n\n function serializeArg(arg: RemoteObject): unknown {\n if (arg.value !== undefined) {\n return arg.value;\n }\n if (arg.preview) {\n const { preview } = arg;\n if (preview.subtype === 'array') {\n return preview.properties\n .filter((p) => /^\\d+$/.test(p.name))\n .sort((a, b) => parseInt(a.name) - parseInt(b.name))\n .map(parseProp);\n }\n const obj: Record<string, unknown> = {};\n for (const prop of preview.properties) {\n obj[prop.name] = parseProp(prop);\n }\n return obj;\n }\n return arg.description ?? `[${arg.type}]`;\n }\n\n const enableRuntime = (sessionId?: string): void => {\n void win.webContents.debugger.sendCommand(\n 'Runtime.enable',\n { generatePreview: true },\n sessionId,\n );\n };\n\n win.webContents.debugger.attach('1.3');\n enableRuntime();\n void win.webContents.debugger.sendCommand('Target.setAutoAttach', {\n autoAttach: true,\n waitForDebuggerOnStart: false,\n flatten: true,\n });\n\n async function serializeArgAsync(arg: RemoteObject, sessionId?: string): Promise<unknown> {\n if (arg.type === 'object' && arg.objectId) {\n try {\n const result: unknown = await win.webContents.debugger.sendCommand(\n 'Runtime.callFunctionOn',\n {\n objectId: arg.objectId,\n functionDeclaration:\n '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; }); }',\n returnByValue: true,\n },\n sessionId,\n );\n const val = (result as { result: { value?: string } }).result.value;\n if (typeof val === 'string') {\n return JSON.parse(val);\n }\n } catch {\n // fall through to preview-based serialization\n }\n }\n return serializeArg(arg);\n }\n\n win.webContents.debugger.on('message', (_event, method, params, sessionId) => {\n if (method === 'Target.attachedToTarget') {\n const { sessionId: childSessionId } = params as { sessionId: string };\n enableRuntime(childSessionId);\n return;\n }\n\n if (method !== 'Runtime.consoleAPICalled') {\n return;\n }\n const { type, args } = params as ConsoleApiCalledParams;\n if (args[0]?.type === 'string' && String(args[0].value).includes('Electron Security Warning')) {\n return;\n }\n const level =\n type === 'warning' ? 'warn' : ['log', 'warn', 'error', 'info'].includes(type) ? type : 'log';\n void Promise.all(args.map((arg) => serializeArgAsync(arg, sessionId as string | undefined))).then(\n (serializedArgs) => {\n win.webContents.send('console-message', { level, args: serializedArgs });\n },\n );\n });\n\n void win.loadURL(`http://localhost:${String(port)}`);\n});\n\napp.on('window-all-closed', () => {\n app.quit();\n});\n"],"mappings":";AAAA,SAAS,KAAK,eAAe,SAAS,aAAa,MAAM,eAAe;AACxE,SAAS,MAAM,eAAe;AAC9B,SAAS,qBAAqB;AAE9B,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,OAAO,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,QAAQ,EAAE;AACvF,IAAM,cAAc,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,kBAAkB,CAAC,GAAG,MAAM,EAAE;AAEhF,IAAI,aAAa;AACf,MAAI,QAAQ,YAAY,WAAW;AACrC;AAEA,IAAI,QAAQ,mBAAmB;AAE/B,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,gBAAgB,CAAC,aAAa,WAAW;AAE/C,SAAS,WAAW,KAAsB;AACxC,MAAI;AACF,UAAM,EAAE,SAAS,IAAI,IAAI,IAAI,GAAG;AAChC,WAAO,cAAc,KAAK,CAAC,MAAM,aAAa,KAAK,SAAS,SAAS,MAAM,CAAC,CAAC;AAAA,EAC/E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,eAAqB;AAE5B,UAAQ,eAAe,WAAW,kBAAkB,CAAC,SAAS,aAAa;AACzE,UAAM,UAAoC,CAAC;AAE3C,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,mBAAmB,CAAC,CAAC,GAAG;AACxE,UAAI,CAAC,YAAY,IAAI,IAAI,YAAY,CAAC,GAAG;AACvC,gBAAQ,GAAG,IAAI;AAAA,MACjB;AAAA,IACF;AAEA,QAAI,WAAW,QAAQ,GAAG,KAAK,QAAQ,YAAY,GAAG;AACpD,cAAQ,YAAY,IAAI,QAAQ,YAAY,EAAE,IAAI,CAAC,WAAW;AAC5D,YAAI,CAAC,aAAa,KAAK,MAAM,GAAG;AAC9B,iBAAO,SAAS;AAAA,QAClB;AAEA,eAAO,OAAO,QAAQ,iBAAiB,eAAe;AAAA,MACxD,CAAC;AAAA,IACH;AAEA,aAAS,EAAE,iBAAiB,QAAQ,CAAC;AAAA,EACvC,CAAC;AAID,cAAY,MAAM;AAChB,UAAM,YAAY;AAChB,iBAAW,UAAU,eAAe;AAClC,YAAI;AACF,gBAAM,UAAU,MAAM,QAAQ,eAAe,QAAQ,IAAI,EAAE,OAAO,CAAC;AACnE,qBAAW,UAAU,SAAS;AAC5B,gBAAI,OAAO,aAAa,kBAAkB;AACxC,oBAAM,MAAM,OAAO,UAAU;AAC7B,oBAAM,OAAO,IAAI,WAAW,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI;AAClD,oBAAM,QAAQ,eAAe,QAAQ,IAAI;AAAA,gBACvC,KAAK,WAAW,IAAI;AAAA,gBACpB,MAAM,OAAO;AAAA,gBACb,OAAO,OAAO;AAAA,gBACd,QAAQ;AAAA,gBACR,MAAM,OAAO,QAAQ;AAAA,gBACrB,QAAQ;AAAA,gBACR,GAAI,OAAO,aAAa,UAAa,EAAE,UAAU,OAAO,SAAS;AAAA,gBACjE,GAAI,OAAO,mBAAmB,UAAa;AAAA,kBACzC,gBAAgB,OAAO;AAAA,gBACzB;AAAA,gBACA,UAAU;AAAA,cACZ,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,GAAG;AAAA,EACL,GAAG,GAAI;AACT;AAEA,KAAK,IAAI,UAAU,EAAE,KAAK,MAAM;AAC9B,OAAK,mBAAmB,IAAI;AAE5B,eAAa;AAEb,QAAM,WAAW,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,UAAU;AACzE,QAAM,OAAO,YAAY,eAAe,QAAQ;AAEhD,MAAI,QAAQ,aAAa,YAAY,CAAC,KAAK,QAAQ,GAAG;AACpD,QAAI,MAAM,QAAQ,IAAI;AAAA,EACxB;AAEA,QAAM,MAAM,IAAI,cAAc;AAAA,IAC5B,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP;AAAA,IACA,gBAAgB;AAAA,MACd,iBAAiB;AAAA,MACjB,kBAAkB;AAAA,MAClB,SAAS;AAAA,MACT,SAAS,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,aAAa;AAAA,IACtE;AAAA,EACF,CAAC;AAED,UAAQ,GAAG,iBAAiB,MAAM;AAChC,QAAI,YAAY,aAAa;AAAA,EAC/B,CAAC;AA4BD,WAAS,UAAU,MAAgC;AACjD,QAAI,KAAK,SAAS,UAAU;AAC1B,aAAO,OAAO,KAAK,KAAK;AAAA,IAC1B;AACA,QAAI,KAAK,SAAS,WAAW;AAC3B,aAAO,KAAK,UAAU;AAAA,IACxB;AACA,QAAI,KAAK,SAAS,aAAa;AAC7B,aAAO;AAAA,IACT;AACA,QAAI,KAAK,SAAS,YAAY,KAAK,UAAU,QAAQ;AACnD,aAAO;AAAA,IACT;AACA,QAAI,KAAK,SAAS,YAAY,KAAK,cAAc;AAC/C,YAAM,EAAE,aAAa,IAAI;AACzB,UAAI,aAAa,YAAY,SAAS;AACpC,eAAO,aAAa,WACjB,OAAO,CAAC,MAAM,QAAQ,KAAK,EAAE,IAAI,CAAC,EAClC,KAAK,CAAC,GAAG,MAAM,SAAS,EAAE,IAAI,IAAI,SAAS,EAAE,IAAI,CAAC,EAClD,IAAI,SAAS;AAAA,MAClB;AACA,YAAM,MAA+B,CAAC;AACtC,iBAAW,KAAK,aAAa,YAAY;AACvC,YAAI,EAAE,IAAI,IAAI,UAAU,CAAC;AAAA,MAC3B;AACA,aAAO;AAAA,IACT;AACA,WAAO,KAAK,SAAS,IAAI,KAAK,IAAI;AAAA,EACpC;AAEA,WAAS,aAAa,KAA4B;AAChD,QAAI,IAAI,UAAU,QAAW;AAC3B,aAAO,IAAI;AAAA,IACb;AACA,QAAI,IAAI,SAAS;AACf,YAAM,EAAE,QAAQ,IAAI;AACpB,UAAI,QAAQ,YAAY,SAAS;AAC/B,eAAO,QAAQ,WACZ,OAAO,CAAC,MAAM,QAAQ,KAAK,EAAE,IAAI,CAAC,EAClC,KAAK,CAAC,GAAG,MAAM,SAAS,EAAE,IAAI,IAAI,SAAS,EAAE,IAAI,CAAC,EAClD,IAAI,SAAS;AAAA,MAClB;AACA,YAAM,MAA+B,CAAC;AACtC,iBAAW,QAAQ,QAAQ,YAAY;AACrC,YAAI,KAAK,IAAI,IAAI,UAAU,IAAI;AAAA,MACjC;AACA,aAAO;AAAA,IACT;AACA,WAAO,IAAI,eAAe,IAAI,IAAI,IAAI;AAAA,EACxC;AAEA,QAAM,gBAAgB,CAAC,cAA6B;AAClD,SAAK,IAAI,YAAY,SAAS;AAAA,MAC5B;AAAA,MACA,EAAE,iBAAiB,KAAK;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,YAAY,SAAS,OAAO,KAAK;AACrC,gBAAc;AACd,OAAK,IAAI,YAAY,SAAS,YAAY,wBAAwB;AAAA,IAChE,YAAY;AAAA,IACZ,wBAAwB;AAAA,IACxB,SAAS;AAAA,EACX,CAAC;AAED,iBAAe,kBAAkB,KAAmB,WAAsC;AACxF,QAAI,IAAI,SAAS,YAAY,IAAI,UAAU;AACzC,UAAI;AACF,cAAM,SAAkB,MAAM,IAAI,YAAY,SAAS;AAAA,UACrD;AAAA,UACA;AAAA,YACE,UAAU,IAAI;AAAA,YACd,qBACE;AAAA,YACF,eAAe;AAAA,UACjB;AAAA,UACA;AAAA,QACF;AACA,cAAM,MAAO,OAA0C,OAAO;AAC9D,YAAI,OAAO,QAAQ,UAAU;AAC3B,iBAAO,KAAK,MAAM,GAAG;AAAA,QACvB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO,aAAa,GAAG;AAAA,EACzB;AAEA,MAAI,YAAY,SAAS,GAAG,WAAW,CAAC,QAAQ,QAAQ,QAAQ,cAAc;AAC5E,QAAI,WAAW,2BAA2B;AACxC,YAAM,EAAE,WAAW,eAAe,IAAI;AACtC,oBAAc,cAAc;AAC5B;AAAA,IACF;AAEA,QAAI,WAAW,4BAA4B;AACzC;AAAA,IACF;AACA,UAAM,EAAE,MAAM,MAAAA,MAAK,IAAI;AACvB,QAAIA,MAAK,CAAC,GAAG,SAAS,YAAY,OAAOA,MAAK,CAAC,EAAE,KAAK,EAAE,SAAS,2BAA2B,GAAG;AAC7F;AAAA,IACF;AACA,UAAM,QACJ,SAAS,YAAY,SAAS,CAAC,OAAO,QAAQ,SAAS,MAAM,EAAE,SAAS,IAAI,IAAI,OAAO;AACzF,SAAK,QAAQ,IAAIA,MAAK,IAAI,CAAC,QAAQ,kBAAkB,KAAK,SAA+B,CAAC,CAAC,EAAE;AAAA,MAC3F,CAAC,mBAAmB;AAClB,YAAI,YAAY,KAAK,mBAAmB,EAAE,OAAO,MAAM,eAAe,CAAC;AAAA,MACzE;AAAA,IACF;AAAA,EACF,CAAC;AAED,OAAK,IAAI,QAAQ,oBAAoB,OAAO,IAAI,CAAC,EAAE;AACrD,CAAC;AAED,IAAI,GAAG,qBAAqB,MAAM;AAChC,MAAI,KAAK;AACX,CAAC;","names":["args"]}
1
+ {"version":3,"sources":["../../src/electron/main.ts"],"sourcesContent":["import { app, BrowserWindow, session, nativeImage, Menu, ipcMain, screen } from 'electron';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst args = process.argv.slice(2);\nconst port = parseInt(args.find((a) => a.startsWith('--port='))?.slice(7) ?? '3121', 10);\nconst userDataDir = args.find((a) => a.startsWith('--user-data-dir='))?.slice(16);\n\nif (userDataDir) {\n app.setPath('userData', userDataDir);\n}\n\napp.setName('Kizen App Builder');\n\nconst CSP_HEADERS = new Set([\n 'content-security-policy',\n 'content-security-policy-report-only',\n 'x-frame-options',\n]);\n\nconst KIZEN_DOMAINS = ['kizen.dev', 'kizen.com'];\n\nfunction isKizenUrl(url: string): boolean {\n try {\n const { hostname } = new URL(url);\n return KIZEN_DOMAINS.some((d) => hostname === d || hostname.endsWith('.' + d));\n } catch {\n return false;\n }\n}\n\nfunction setupSession(): void {\n // Strip CSP headers and fix SameSite cookies on Kizen responses.\n session.defaultSession.webRequest.onHeadersReceived((details, callback) => {\n const headers: Record<string, string[]> = {};\n\n for (const [key, value] of Object.entries(details.responseHeaders ?? {})) {\n if (!CSP_HEADERS.has(key.toLowerCase())) {\n headers[key] = value;\n }\n }\n\n if (isKizenUrl(details.url) && headers['set-cookie']) {\n headers['set-cookie'] = headers['set-cookie'].map((cookie) => {\n if (!/SameSite=/i.test(cookie)) {\n return cookie + '; SameSite=None; Secure';\n }\n\n return cookie.replace(/SameSite=\\w+/i, 'SameSite=None');\n });\n }\n\n callback({ responseHeaders: headers });\n });\n\n // Periodically re-patch existing Kizen cookies that arrived without SameSite=None.\n // Needed for cookies set before the interceptor was active or via JS document.cookie.\n setInterval(() => {\n void (async () => {\n for (const domain of KIZEN_DOMAINS) {\n try {\n const cookies = await session.defaultSession.cookies.get({ domain });\n for (const cookie of cookies) {\n if (cookie.sameSite !== 'no_restriction') {\n const raw = cookie.domain ?? domain;\n const bare = raw.startsWith('.') ? raw.slice(1) : raw;\n await session.defaultSession.cookies.set({\n url: `https://${bare}`,\n name: cookie.name,\n value: cookie.value,\n domain: raw,\n path: cookie.path ?? '/',\n secure: true,\n ...(cookie.httpOnly !== undefined && { httpOnly: cookie.httpOnly }),\n ...(cookie.expirationDate !== undefined && {\n expirationDate: cookie.expirationDate,\n }),\n sameSite: 'no_restriction',\n });\n }\n }\n } catch {\n /* ignore */\n }\n }\n })();\n }, 2000);\n}\n\nvoid app.whenReady().then(() => {\n Menu.setApplicationMenu(null);\n\n setupSession();\n\n const iconPath = join(dirname(fileURLToPath(import.meta.url)), 'icon.png');\n const icon = nativeImage.createFromPath(iconPath);\n\n if (process.platform === 'darwin' && !icon.isEmpty()) {\n app.dock?.setIcon(icon);\n }\n\n const { width, height } = screen.getPrimaryDisplay().workAreaSize;\n\n const win = new BrowserWindow({\n width,\n height,\n title: 'Kizen App Builder',\n icon,\n webPreferences: {\n nodeIntegration: false,\n contextIsolation: true,\n sandbox: false,\n preload: join(dirname(fileURLToPath(import.meta.url)), 'preload.cjs'),\n },\n });\n\n ipcMain.on('open-devtools', () => {\n win.webContents.openDevTools();\n });\n\n interface PropertyPreview {\n name: string;\n type: string;\n value?: string;\n subtype?: string;\n valuePreview?: ObjectPreview;\n }\n interface ObjectPreview {\n type: string;\n subtype?: string;\n overflow: boolean;\n properties: PropertyPreview[];\n }\n interface RemoteObject {\n type: string;\n subtype?: string;\n value?: unknown;\n description?: string;\n preview?: ObjectPreview;\n objectId?: string;\n }\n interface ConsoleApiCalledParams {\n type: string;\n args: RemoteObject[];\n }\n\n function parseProp(prop: PropertyPreview): unknown {\n if (prop.type === 'number') {\n return Number(prop.value);\n }\n if (prop.type === 'boolean') {\n return prop.value === 'true';\n }\n if (prop.type === 'undefined') {\n return undefined;\n }\n if (prop.type === 'object' && prop.value === 'null') {\n return null;\n }\n if (prop.type === 'object' && prop.valuePreview) {\n const { valuePreview } = prop;\n if (valuePreview.subtype === 'array') {\n return valuePreview.properties\n .filter((p) => /^\\d+$/.test(p.name))\n .sort((a, b) => parseInt(a.name) - parseInt(b.name))\n .map(parseProp);\n }\n const obj: Record<string, unknown> = {};\n for (const p of valuePreview.properties) {\n obj[p.name] = parseProp(p);\n }\n return obj;\n }\n return prop.value ?? `[${prop.type}]`;\n }\n\n function serializeArg(arg: RemoteObject): unknown {\n if (arg.value !== undefined) {\n return arg.value;\n }\n if (arg.preview) {\n const { preview } = arg;\n if (preview.subtype === 'array') {\n return preview.properties\n .filter((p) => /^\\d+$/.test(p.name))\n .sort((a, b) => parseInt(a.name) - parseInt(b.name))\n .map(parseProp);\n }\n const obj: Record<string, unknown> = {};\n for (const prop of preview.properties) {\n obj[prop.name] = parseProp(prop);\n }\n return obj;\n }\n return arg.description ?? `[${arg.type}]`;\n }\n\n const enableRuntime = (sessionId?: string): void => {\n void win.webContents.debugger.sendCommand(\n 'Runtime.enable',\n { generatePreview: true },\n sessionId,\n );\n };\n\n win.webContents.debugger.attach('1.3');\n enableRuntime();\n void win.webContents.debugger.sendCommand('Target.setAutoAttach', {\n autoAttach: true,\n waitForDebuggerOnStart: false,\n flatten: true,\n });\n\n async function serializeArgAsync(arg: RemoteObject, sessionId?: string): Promise<unknown> {\n if (arg.type === 'object' && arg.objectId) {\n try {\n const result: unknown = await win.webContents.debugger.sendCommand(\n 'Runtime.callFunctionOn',\n {\n objectId: arg.objectId,\n functionDeclaration:\n '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; }); }',\n returnByValue: true,\n },\n sessionId,\n );\n const val = (result as { result: { value?: string } }).result.value;\n if (typeof val === 'string') {\n return JSON.parse(val);\n }\n } catch {\n // fall through to preview-based serialization\n }\n }\n return serializeArg(arg);\n }\n\n win.webContents.debugger.on('message', (_event, method, params, sessionId) => {\n if (method === 'Target.attachedToTarget') {\n const { sessionId: childSessionId } = params as { sessionId: string };\n enableRuntime(childSessionId);\n return;\n }\n\n if (method !== 'Runtime.consoleAPICalled') {\n return;\n }\n const { type, args } = params as ConsoleApiCalledParams;\n if (args[0]?.type === 'string' && String(args[0].value).includes('Electron Security Warning')) {\n return;\n }\n const level =\n type === 'warning' ? 'warn' : ['log', 'warn', 'error', 'info'].includes(type) ? type : 'log';\n void Promise.all(\n args.map((arg) => serializeArgAsync(arg, sessionId as string | undefined)),\n ).then((serializedArgs) => {\n win.webContents.send('console-message', { level, args: serializedArgs });\n });\n });\n\n void win.loadURL(`http://localhost:${String(port)}`);\n});\n\napp.on('window-all-closed', () => {\n app.quit();\n});\n"],"mappings":"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","names":["args"]}
package/dist/index.js CHANGED
@@ -269,33 +269,14 @@ function createProxyCache() {
269
269
  };
270
270
  }
271
271
 
272
- // src/lib/config.ts
273
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
274
- import { join as join4 } from "path";
275
- async function loadConfig(outputDir) {
276
- try {
277
- const content = await readFile2(join4(outputDir, "config.json"), "utf-8");
278
- return JSON.parse(content);
279
- } catch {
280
- return {};
281
- }
282
- }
283
- async function saveConfig(outputDir, config) {
284
- await mkdir2(outputDir, { recursive: true });
285
- await writeFile2(join4(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
286
- }
287
-
288
- // src/ui/CredentialSetupUI.tsx
289
- import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
290
- import { Box as Box3, Text as Text3, useInput } from "ink";
291
-
292
272
  // src/lib/credentials.ts
293
- import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
273
+ import { mkdir as mkdir2, readFile as readFile2, readdir as readdir2, writeFile as writeFile2 } from "fs/promises";
294
274
  import { homedir } from "os";
295
- import { dirname, join as join5 } from "path";
275
+ import { dirname, join as join4 } from "path";
296
276
  var ENVIRONMENTS = ["go", "fmo", "staging", "integration", "test1"];
297
- var GLOBAL_CREDENTIALS_DIR = join5(homedir(), ".kizenappbuilder");
298
- var GLOBAL_CREDENTIALS_PATH = join5(GLOBAL_CREDENTIALS_DIR, "credentials.json");
277
+ var GLOBAL_CREDENTIALS_DIR = join4(homedir(), ".kizenappbuilder");
278
+ var GLOBAL_CREDENTIALS_PATH = join4(GLOBAL_CREDENTIALS_DIR, "credentials.json");
279
+ var DEFAULT_PROFILE_NAME = "credentials";
299
280
  function isValidEnvironment(value) {
300
281
  return ENVIRONMENTS.includes(value);
301
282
  }
@@ -313,7 +294,7 @@ function parseCredentials(raw) {
313
294
  };
314
295
  }
315
296
  async function loadCredentialsFromFile(filePath) {
316
- const content = await readFile3(filePath, "utf-8");
297
+ const content = await readFile2(filePath, "utf-8");
317
298
  return parseCredentials(JSON.parse(content));
318
299
  }
319
300
  async function loadGlobalCredentials() {
@@ -324,11 +305,63 @@ async function loadGlobalCredentials() {
324
305
  }
325
306
  }
326
307
  async function saveGlobalCredentials(credentials) {
327
- await mkdir3(dirname(GLOBAL_CREDENTIALS_PATH), { recursive: true });
328
- await writeFile3(GLOBAL_CREDENTIALS_PATH, JSON.stringify(credentials, null, 2), "utf-8");
308
+ await mkdir2(dirname(GLOBAL_CREDENTIALS_PATH), { recursive: true });
309
+ await writeFile2(GLOBAL_CREDENTIALS_PATH, JSON.stringify(credentials, null, 2), "utf-8");
310
+ }
311
+ function getProfilePath(name) {
312
+ return join4(GLOBAL_CREDENTIALS_DIR, `${name}.json`);
313
+ }
314
+ async function listCredentialProfiles() {
315
+ const profiles = [
316
+ { name: DEFAULT_PROFILE_NAME, path: GLOBAL_CREDENTIALS_PATH, isDefault: true }
317
+ ];
318
+ try {
319
+ const entries = await readdir2(GLOBAL_CREDENTIALS_DIR);
320
+ for (const entry of entries) {
321
+ if (!entry.endsWith(".json")) continue;
322
+ const name = entry.slice(0, -5);
323
+ if (name === DEFAULT_PROFILE_NAME) continue;
324
+ profiles.push({ name, path: join4(GLOBAL_CREDENTIALS_DIR, entry), isDefault: false });
325
+ }
326
+ } catch {
327
+ }
328
+ return profiles;
329
+ }
330
+ async function saveCredentialProfile(name, credentials) {
331
+ if (name === DEFAULT_PROFILE_NAME) {
332
+ await saveGlobalCredentials(credentials);
333
+ return;
334
+ }
335
+ await mkdir2(GLOBAL_CREDENTIALS_DIR, { recursive: true });
336
+ await writeFile2(getProfilePath(name), JSON.stringify(credentials, null, 2), "utf-8");
337
+ }
338
+ async function loadCredentialProfile(name) {
339
+ try {
340
+ return await loadCredentialsFromFile(getProfilePath(name));
341
+ } catch {
342
+ return null;
343
+ }
344
+ }
345
+
346
+ // src/lib/config.ts
347
+ import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
348
+ import { join as join5 } from "path";
349
+ async function loadConfig(outputDir) {
350
+ try {
351
+ const content = await readFile3(join5(outputDir, "config.json"), "utf-8");
352
+ return JSON.parse(content);
353
+ } catch {
354
+ return {};
355
+ }
356
+ }
357
+ async function saveConfig(outputDir, config) {
358
+ await mkdir3(outputDir, { recursive: true });
359
+ await writeFile3(join5(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
329
360
  }
330
361
 
331
362
  // src/ui/CredentialSetupUI.tsx
363
+ import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
364
+ import { Box as Box3, Text as Text3, useInput } from "ink";
332
365
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
333
366
  var FIELDS = ["apiKey", "userId", "businessId"];
334
367
  var FIELD_LABELS = {
@@ -339,32 +372,39 @@ var FIELD_LABELS = {
339
372
  var Hint = ({ text }) => /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: text });
340
373
  var CredentialSetupUI = ({
341
374
  initialMode,
375
+ showProfileManager,
342
376
  onComplete,
343
377
  onCancel
344
378
  }) => {
345
379
  const [phase, setPhase] = useState2(
346
- initialMode === "global" ? { type: "loading" } : { type: "mode-select", cursor: 0 }
380
+ initialMode === "global" && !showProfileManager ? { type: "loading" } : initialMode === "global" && showProfileManager ? { type: "profile-loading" } : { type: "mode-select", cursor: 0 }
347
381
  );
348
382
  const [inputBuffer, setInputBuffer] = useState2("");
349
383
  const [error, setError] = useState2(null);
384
+ const [activeProfileName, setActiveProfileName] = useState2(void 0);
350
385
  const handleModeChosen = useCallback(
351
386
  async (mode) => {
352
387
  if (mode === "local") {
353
388
  onComplete({ mode: "local", credentials: null });
354
389
  return;
355
390
  }
356
- setPhase({ type: "loading" });
357
- const existing = await loadGlobalCredentials();
358
- const envCursor2 = existing ? Math.max(0, ENVIRONMENTS.indexOf(existing.environment)) : 0;
359
- setPhase({
360
- type: "creds-entry",
361
- field: 0,
362
- values: existing ?? {},
363
- envCursor: envCursor2
364
- });
391
+ setPhase({ type: "profile-loading" });
365
392
  },
366
393
  [onComplete]
367
394
  );
395
+ const handleProfileChosen = useCallback(async (profile) => {
396
+ if (profile === null) {
397
+ setPhase({ type: "profile-loading" });
398
+ const profiles = await listCredentialProfiles();
399
+ setPhase({ type: "name-entry", nameBuffer: "", existingProfiles: profiles });
400
+ return;
401
+ }
402
+ setActiveProfileName(profile.isDefault ? void 0 : profile.name);
403
+ setPhase({ type: "loading" });
404
+ const existing = profile.isDefault ? await loadGlobalCredentials() : await loadCredentialProfile(profile.name);
405
+ const envCursor2 = existing ? Math.max(0, ENVIRONMENTS.indexOf(existing.environment)) : 0;
406
+ setPhase({ type: "creds-entry", field: 0, values: existing ?? {}, envCursor: envCursor2 });
407
+ }, []);
368
408
  const handleSave = useCallback(
369
409
  async (values2, envCursor2) => {
370
410
  const environment = ENVIRONMENTS[envCursor2] ?? "go";
@@ -376,20 +416,32 @@ var CredentialSetupUI = ({
376
416
  };
377
417
  setPhase({ type: "saving" });
378
418
  try {
379
- await saveGlobalCredentials(credentials);
380
- onComplete({ mode: "global", credentials });
419
+ const profileName = activeProfileName ?? DEFAULT_PROFILE_NAME;
420
+ await saveCredentialProfile(profileName, credentials);
421
+ onComplete({
422
+ mode: "global",
423
+ credentials,
424
+ ...activeProfileName !== void 0 && { profileName: activeProfileName }
425
+ });
381
426
  } catch (err) {
382
427
  setError(err instanceof Error ? err.message : String(err));
383
428
  setPhase({ type: "creds-entry", field: 0, values: values2, envCursor: envCursor2 });
384
429
  }
385
430
  },
386
- [onComplete]
431
+ [onComplete, activeProfileName]
387
432
  );
388
433
  useInput((input, key) => {
389
434
  if (key.ctrl && input === "c") {
390
435
  process.exit(0);
391
436
  }
392
437
  if (key.escape) {
438
+ if (phase.type === "creds-entry" || phase.type === "name-entry") {
439
+ if (initialMode === "global" && showProfileManager) {
440
+ setPhase({ type: "profile-loading" });
441
+ void loadProfileList();
442
+ return;
443
+ }
444
+ }
393
445
  onCancel?.();
394
446
  return;
395
447
  }
@@ -404,6 +456,49 @@ var CredentialSetupUI = ({
404
456
  }
405
457
  return;
406
458
  }
459
+ if (phase.type === "profile-select") {
460
+ const { profiles, cursor } = phase;
461
+ const addNewIdx = profiles.length;
462
+ const totalItems = profiles.length + 1;
463
+ if (key.upArrow) {
464
+ setPhase({ ...phase, cursor: (cursor - 1 + totalItems) % totalItems });
465
+ } else if (key.downArrow) {
466
+ setPhase({ ...phase, cursor: (cursor + 1) % totalItems });
467
+ } else if (key.return) {
468
+ if (cursor === addNewIdx) {
469
+ setPhase({ type: "name-entry", nameBuffer: "", existingProfiles: profiles });
470
+ } else {
471
+ void handleProfileChosen(profiles[cursor] ?? profiles[0]);
472
+ }
473
+ }
474
+ return;
475
+ }
476
+ if (phase.type === "name-entry") {
477
+ const { nameBuffer, nameError: _nameError, existingProfiles } = phase;
478
+ if (key.backspace || key.delete) {
479
+ setPhase({ type: "name-entry", nameBuffer: nameBuffer.slice(0, -1), existingProfiles });
480
+ } else if (key.return) {
481
+ const trimmed = nameBuffer.trim();
482
+ if (!trimmed) {
483
+ setPhase({ ...phase, nameError: "Name cannot be empty" });
484
+ return;
485
+ }
486
+ if (trimmed === DEFAULT_PROFILE_NAME) {
487
+ setPhase({ ...phase, nameError: `"${DEFAULT_PROFILE_NAME}" is reserved` });
488
+ return;
489
+ }
490
+ if (existingProfiles.some((p) => p.name === trimmed)) {
491
+ setPhase({ ...phase, nameError: `Profile "${trimmed}" already exists` });
492
+ return;
493
+ }
494
+ setActiveProfileName(trimmed);
495
+ setPhase({ type: "creds-entry", field: 0, values: {}, envCursor: 0 });
496
+ setInputBuffer("");
497
+ } else if (input && !key.ctrl && !key.meta) {
498
+ setPhase({ type: "name-entry", nameBuffer: nameBuffer + input, existingProfiles });
499
+ }
500
+ return;
501
+ }
407
502
  if (phase.type === "creds-entry") {
408
503
  const { field, values: values2, envCursor: envCursor2 } = phase;
409
504
  const isEnvField2 = field === FIELDS.length;
@@ -448,9 +543,22 @@ var CredentialSetupUI = ({
448
543
  }
449
544
  }
450
545
  });
546
+ const loadProfileList = useCallback(async () => {
547
+ const profiles = await listCredentialProfiles();
548
+ setPhase({ type: "profile-select", profiles, cursor: 0 });
549
+ }, []);
451
550
  useEffect2(() => {
452
- if (initialMode === "global") {
453
- void handleModeChosen("global");
551
+ if (phase.type === "profile-loading") {
552
+ void loadProfileList();
553
+ }
554
+ }, [phase.type, loadProfileList]);
555
+ useEffect2(() => {
556
+ if (initialMode === "global" && !showProfileManager) {
557
+ void (async () => {
558
+ const existing = await loadGlobalCredentials();
559
+ const envCursor2 = existing ? Math.max(0, ENVIRONMENTS.indexOf(existing.environment)) : 0;
560
+ setPhase({ type: "creds-entry", field: 0, values: existing ?? {}, envCursor: envCursor2 });
561
+ })();
454
562
  }
455
563
  }, []);
456
564
  useEffect2(() => {
@@ -458,8 +566,9 @@ var CredentialSetupUI = ({
458
566
  setInputBuffer(phase.values.apiKey ?? "");
459
567
  }
460
568
  }, [phase.type]);
461
- if (phase.type === "loading" || phase.type === "saving") {
462
- return /* @__PURE__ */ jsx3(Box3, { paddingY: 1, paddingX: 2, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: phase.type === "loading" ? "Loading credentials\u2026" : "Saving credentials\u2026" }) });
569
+ if (phase.type === "loading" || phase.type === "saving" || phase.type === "profile-loading") {
570
+ const label = phase.type === "saving" ? "Saving credentials\u2026" : phase.type === "profile-loading" ? "Loading profiles\u2026" : "Loading credentials\u2026";
571
+ return /* @__PURE__ */ jsx3(Box3, { paddingY: 1, paddingX: 2, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: label }) });
463
572
  }
464
573
  if (phase.type === "done") {
465
574
  return null;
@@ -489,8 +598,58 @@ var CredentialSetupUI = ({
489
598
  /* @__PURE__ */ jsx3(Hint, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Ctrl+C to quit" })
490
599
  ] });
491
600
  }
601
+ if (phase.type === "profile-select") {
602
+ const { profiles, cursor } = phase;
603
+ const addNewIdx = profiles.length;
604
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
605
+ /* @__PURE__ */ jsx3(Logo, {}),
606
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, children: [
607
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "Kizen App Builder" }),
608
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(24) })
609
+ ] }),
610
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
611
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Credential profiles" }),
612
+ profiles.map((profile, i) => {
613
+ const selected = cursor === i;
614
+ return /* @__PURE__ */ jsxs2(Box3, { gap: 2, children: [
615
+ /* @__PURE__ */ jsx3(Text3, { ...selected && { color: "cyan" }, children: selected ? "\u276F" : " " }),
616
+ /* @__PURE__ */ jsx3(Text3, { bold: selected, ...selected && { color: "cyan" }, children: profile.isDefault ? "Default" : profile.name }),
617
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: profile.path })
618
+ ] }, profile.name);
619
+ }),
620
+ /* @__PURE__ */ jsxs2(Box3, { gap: 2, children: [
621
+ /* @__PURE__ */ jsx3(Text3, { ...cursor === addNewIdx && { color: "cyan" }, children: cursor === addNewIdx ? "\u276F" : " " }),
622
+ /* @__PURE__ */ jsx3(Text3, { bold: cursor === addNewIdx, ...cursor === addNewIdx && { color: "cyan" }, children: "+ Add new profile" })
623
+ ] })
624
+ ] }),
625
+ /* @__PURE__ */ jsx3(Hint, { text: "\u2191\u2193 to move \xB7 Enter to select \xB7 Esc to cancel" })
626
+ ] });
627
+ }
628
+ if (phase.type === "name-entry") {
629
+ const { nameBuffer, nameError } = phase;
630
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
631
+ /* @__PURE__ */ jsx3(Logo, {}),
632
+ /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, children: [
633
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "Kizen App Builder" }),
634
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(24) })
635
+ ] }),
636
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", gap: 0, children: /* @__PURE__ */ jsx3(Text3, { bold: true, children: "New credential profile" }) }),
637
+ nameError && /* @__PURE__ */ jsxs2(Text3, { color: "red", children: [
638
+ "Error: ",
639
+ nameError
640
+ ] }),
641
+ /* @__PURE__ */ jsxs2(Box3, { gap: 2, children: [
642
+ /* @__PURE__ */ jsx3(Box3, { width: 12, children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "Profile name" }) }),
643
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: ">" }),
644
+ /* @__PURE__ */ jsx3(Text3, { children: nameBuffer }),
645
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "\u2588" })
646
+ ] }),
647
+ /* @__PURE__ */ jsx3(Hint, { text: "Type a name \xB7 Enter to confirm \xB7 Esc to go back" })
648
+ ] });
649
+ }
492
650
  const { field: activeField, values, envCursor } = phase;
493
651
  const isEnvField = activeField === FIELDS.length;
652
+ const profileLabel = activeProfileName !== void 0 ? activeProfileName : DEFAULT_PROFILE_NAME;
494
653
  return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingY: 1, paddingX: 2, gap: 1, children: [
495
654
  /* @__PURE__ */ jsx3(Logo, {}),
496
655
  /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginTop: 1, children: [
@@ -498,10 +657,10 @@ var CredentialSetupUI = ({
498
657
  /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2500".repeat(24) })
499
658
  ] }),
500
659
  /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", gap: 0, children: [
501
- /* @__PURE__ */ jsx3(Text3, { bold: true, children: "Global credentials" }),
660
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: activeProfileName !== void 0 ? `Profile: ${activeProfileName}` : "Global credentials" }),
502
661
  /* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
503
662
  "Saved to: ",
504
- GLOBAL_CREDENTIALS_PATH
663
+ activeProfileName !== void 0 ? `~/.kizenappbuilder/${activeProfileName}.json` : GLOBAL_CREDENTIALS_PATH
505
664
  ] })
506
665
  ] }),
507
666
  error && /* @__PURE__ */ jsxs2(Text3, { color: "red", children: [
@@ -531,7 +690,11 @@ var CredentialSetupUI = ({
531
690
  }) })
532
691
  ] })
533
692
  ] }),
534
- 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" })
693
+ 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" }),
694
+ /* @__PURE__ */ jsxs2(Text3, { dimColor: true, children: [
695
+ "Profile: ",
696
+ profileLabel
697
+ ] })
535
698
  ] });
536
699
  };
537
700
 
@@ -591,7 +754,7 @@ async function fileExists(filePath) {
591
754
  return false;
592
755
  }
593
756
  }
594
- function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef) {
757
+ function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast) {
595
758
  const proxyCache = createProxyCache();
596
759
  return (req, res) => {
597
760
  void (async () => {
@@ -603,6 +766,34 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
603
766
  res.end(credentialsRef.current !== null ? JSON.stringify(credentialsRef.current) : "{}");
604
767
  return;
605
768
  }
769
+ if (url === "/api/credential-profiles" && req.method === "GET") {
770
+ const profiles = await listCredentialProfiles();
771
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
772
+ res.end(JSON.stringify({ profiles, active: activeProfileRef.current }));
773
+ return;
774
+ }
775
+ if (url === "/api/credentials/switch" && req.method === "POST") {
776
+ const chunks = [];
777
+ for await (const chunk of req) {
778
+ chunks.push(chunk);
779
+ }
780
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf-8") || "{}");
781
+ const profileName = body.profile;
782
+ const creds = profileName === void 0 || profileName === "" ? await loadGlobalCredentials() : await loadCredentialProfile(profileName);
783
+ if (creds !== null) {
784
+ credentialsRef.current = creds;
785
+ activeProfileRef.current = profileName || void 0;
786
+ const active = activeProfileRef.current;
787
+ await saveConfig(outputDir, {
788
+ credentialMode: "global",
789
+ ...active !== void 0 && { activeCredentialProfile: active }
790
+ });
791
+ broadcast({ type: "credentials-updated", credentials: creds });
792
+ }
793
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
794
+ res.end(JSON.stringify({ ok: creds !== null }));
795
+ return;
796
+ }
606
797
  if (url === "/api/bundle") {
607
798
  const bundlePath = join6(process.cwd(), ".kizenapp", "bundle.json");
608
799
  try {
@@ -686,10 +877,20 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
686
877
  res.writeHead(200, { "Content-Type": mimeType });
687
878
  createReadStream(resolvedPath).pipe(res);
688
879
  } catch (err) {
689
- createProxyLog({
690
- kind: "info",
691
- message: `Error handling ${url}: ${err instanceof Error ? err.message : String(err)}`
692
- });
880
+ if (url.startsWith("/api/proxy")) {
881
+ createProxyLog({
882
+ kind: "request",
883
+ method: req.method ?? "GET",
884
+ status: 502,
885
+ fromCache: false,
886
+ url: url.slice("/api/proxy".length) || "/"
887
+ });
888
+ } else {
889
+ createProxyLog({
890
+ kind: "info",
891
+ message: `Error handling ${url}: ${err instanceof Error ? err.message : String(err)}`
892
+ });
893
+ }
693
894
  if (!res.headersSent) {
694
895
  res.writeHead(502);
695
896
  res.end("Bad Gateway");
@@ -702,10 +903,16 @@ var DevUI = ({
702
903
  port,
703
904
  pluginDir,
704
905
  outputDir,
705
- credentials: initialCredentials
906
+ credentials: initialCredentials,
907
+ credentialMode: initialCredentialMode,
908
+ activeCredentialProfile: initialActiveProfile
706
909
  }) => {
707
910
  const credentialsRef = useRef(initialCredentials);
911
+ const activeProfileRef = useRef(initialActiveProfile);
708
912
  const [credMode, setCredMode] = useState3("main");
913
+ const [credentialMode, setCredentialMode] = useState3(
914
+ initialCredentialMode
915
+ );
709
916
  const [status, setStatus] = useState3("starting");
710
917
  const [errorMessage, setErrorMessage] = useState3(null);
711
918
  const [serverLogHistory, setServerLogHistory] = useState3([]);
@@ -775,8 +982,14 @@ var DevUI = ({
775
982
  const handleCredentialsDone = useCallback2(
776
983
  (result) => {
777
984
  credentialsRef.current = result.credentials;
985
+ activeProfileRef.current = result.profileName;
778
986
  setCredMode("main");
779
- void saveConfig(outputDir, { credentialMode: result.mode });
987
+ setCredentialMode(result.mode);
988
+ const profile = result.profileName;
989
+ void saveConfig(outputDir, {
990
+ credentialMode: result.mode,
991
+ ...profile !== void 0 && { activeCredentialProfile: profile }
992
+ });
780
993
  broadcast({ type: "credentials-updated", credentials: result.credentials });
781
994
  },
782
995
  [outputDir, broadcast]
@@ -862,7 +1075,10 @@ var DevUI = ({
862
1075
  viewerPath,
863
1076
  createServerLog,
864
1077
  createProxyLog,
865
- credentialsRef
1078
+ credentialsRef,
1079
+ activeProfileRef,
1080
+ outputDir,
1081
+ broadcast
866
1082
  );
867
1083
  const server = createServer(handler);
868
1084
  const wss = new WebSocketServer({ server });
@@ -899,6 +1115,8 @@ var DevUI = ({
899
1115
  return /* @__PURE__ */ jsx4(
900
1116
  CredentialSetupUI,
901
1117
  {
1118
+ ...credentialMode !== void 0 && { initialMode: credentialMode },
1119
+ showProfileManager: credentialMode === "global",
902
1120
  onComplete: handleCredentialsDone,
903
1121
  onCancel: () => {
904
1122
  setCredMode("main");
@@ -1032,6 +1250,7 @@ function devCommand(program2) {
1032
1250
  ensureGitignore(pluginDir);
1033
1251
  let credentials = null;
1034
1252
  let credentialMode;
1253
+ let activeCredentialProfile;
1035
1254
  if (options.credentials) {
1036
1255
  credentials = await loadCredentialsFromFile(options.credentials);
1037
1256
  } else {
@@ -1040,16 +1259,31 @@ function devCommand(program2) {
1040
1259
  credentialMode = "local";
1041
1260
  } else if (config.credentialMode === "global") {
1042
1261
  credentialMode = "global";
1043
- credentials = await loadGlobalCredentials();
1262
+ activeCredentialProfile = config.activeCredentialProfile;
1263
+ if (activeCredentialProfile) {
1264
+ credentials = await loadCredentialProfile(activeCredentialProfile);
1265
+ if (!credentials) {
1266
+ credentials = await loadGlobalCredentials();
1267
+ activeCredentialProfile = void 0;
1268
+ }
1269
+ } else {
1270
+ credentials = await loadGlobalCredentials();
1271
+ }
1044
1272
  if (!credentials) {
1045
1273
  const result = await runSetupUI("global");
1046
1274
  credentials = result.credentials;
1275
+ activeCredentialProfile = result.profileName;
1047
1276
  }
1048
1277
  } else {
1049
1278
  const result = await runSetupUI();
1050
1279
  credentials = result.credentials;
1051
1280
  credentialMode = result.mode;
1052
- await saveConfig(outputDir, { credentialMode: result.mode });
1281
+ activeCredentialProfile = result.profileName;
1282
+ const profile = result.profileName;
1283
+ await saveConfig(outputDir, {
1284
+ credentialMode: result.mode,
1285
+ ...profile !== void 0 && { activeCredentialProfile: profile }
1286
+ });
1053
1287
  }
1054
1288
  }
1055
1289
  const { waitUntilExit } = render2(
@@ -1058,7 +1292,8 @@ function devCommand(program2) {
1058
1292
  pluginDir,
1059
1293
  outputDir,
1060
1294
  credentials,
1061
- ...credentialMode !== void 0 && { credentialMode }
1295
+ ...credentialMode !== void 0 && { credentialMode },
1296
+ ...activeCredentialProfile !== void 0 && { activeCredentialProfile }
1062
1297
  }),
1063
1298
  { exitOnCtrlC: false }
1064
1299
  );