@kizenapps/cli 0.11.0 → 0.12.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 (77) hide show
  1. package/dist/index.js +482 -41
  2. package/dist/index.js.map +1 -1
  3. package/dist/viewer/assets/AppDetailPage-BPvWscWJ.js +1 -0
  4. package/dist/viewer/assets/AppDetailPage-DIuYKCTQ.js +1 -0
  5. package/dist/viewer/assets/AppDetailPage-r3jzO9Kf.js +1 -0
  6. package/dist/viewer/assets/BootstrapContext-CExPp3R2.js +1 -0
  7. package/dist/viewer/assets/BootstrapContext-CTQMWXf-.js +1 -0
  8. package/dist/viewer/assets/BootstrapContext-DbnbWmQT.js +1 -0
  9. package/dist/viewer/assets/Card-Bmn1HMWl.js +1 -0
  10. package/dist/viewer/assets/Card-DMz6T3VL.js +1 -0
  11. package/dist/viewer/assets/Card-DsqKctyM.js +1 -0
  12. package/dist/viewer/assets/CodeStepsPage-BHqLfCdV.js +4 -0
  13. package/dist/viewer/assets/CodeStepsPage-BymhEfzO.js +4 -0
  14. package/dist/viewer/assets/CodeStepsPage-CD_MhDwY.js +4 -0
  15. package/dist/viewer/assets/CodeStepsPage-CgI-LqEx.js +4 -0
  16. package/dist/viewer/assets/CodeStepsPage-Dak3saZE.js +5 -0
  17. package/dist/viewer/assets/CodeStepsPage-DdKuH37F.css +1 -0
  18. package/dist/viewer/assets/CodeStepsPage-Zj6DscGd.js +4 -0
  19. package/dist/viewer/assets/ConfigurationPage-Cjy9TH3N.js +1 -0
  20. package/dist/viewer/assets/ConfigurationPage-DmX-6MM-.js +1 -0
  21. package/dist/viewer/assets/ConfigurationPage-J56kCD_e.js +1 -0
  22. package/dist/viewer/assets/CredentialsContext-Xbse1T0q.js +1 -0
  23. package/dist/viewer/assets/CredentialsContext-zl2e9kpM.js +1 -0
  24. package/dist/viewer/assets/DevSidebar-B30mgxSn.js +1 -0
  25. package/dist/viewer/assets/{DevSidebar-BiROAoKF.js → DevSidebar-BhTD2bBO.js} +1 -1
  26. package/dist/viewer/assets/DevSidebar-CsEdCTej.js +1 -0
  27. package/dist/viewer/assets/IconReferencePage-B6j9wlte.js +1 -0
  28. package/dist/viewer/assets/IconReferencePage-CJa9yg9Z.js +1 -0
  29. package/dist/viewer/assets/IconReferencePage-Q5I-lMFQ.js +1 -0
  30. package/dist/viewer/assets/SandboxPage-C0h7GUkw.js +1 -0
  31. package/dist/viewer/assets/SandboxPage-GDcydJnJ.js +1 -0
  32. package/dist/viewer/assets/SandboxPage-JREBQ4id.js +1 -0
  33. package/dist/viewer/assets/SidebarContext-BPkYyxET.js +1 -0
  34. package/dist/viewer/assets/SidebarContext-DVDXFWJI.js +1 -0
  35. package/dist/viewer/assets/SidebarContext-HucmR353.js +1 -0
  36. package/dist/viewer/assets/VersionsPage-B76eugMm.js +1 -0
  37. package/dist/viewer/assets/VersionsPage-BIpB0iBz.js +1 -0
  38. package/dist/viewer/assets/VersionsPage-D0qenxK5.js +1 -0
  39. package/dist/viewer/assets/api-BOqcXygX.js +1 -0
  40. package/dist/viewer/assets/api-DqAiw0w9.js +1 -0
  41. package/dist/viewer/assets/bundleQuery-206rQR6g.js +1 -0
  42. package/dist/viewer/assets/{Card-CPMAuqAb.js → bundleQuery-L4H4wq5q.js} +1 -1
  43. package/dist/viewer/assets/{fa-core-mHW_3JUd.js → fa-core-DaD7O-yO.js} +1 -1
  44. package/dist/viewer/assets/fa-core-wsCxaNB7.js +558 -0
  45. package/dist/viewer/assets/fa-pro-icons-Dzkwxnip.js +1 -0
  46. package/dist/viewer/assets/index-BPiwld5D.js +5 -0
  47. package/dist/viewer/assets/index-BiyeMU_r.css +2 -0
  48. package/dist/viewer/assets/index-BlJv_543.js +5 -0
  49. package/dist/viewer/assets/index-CAYNux5J.js +5 -0
  50. package/dist/viewer/assets/index-D4AnoAXz.js +5 -0
  51. package/dist/viewer/assets/index-D8Koqlb-.js +5 -0
  52. package/dist/viewer/assets/index-DKrC0qkC.js +5 -0
  53. package/dist/viewer/assets/{react-D_K8ni3_.js → react-CkuOm_dq.js} +1 -1
  54. package/dist/viewer/assets/react-CyWK9rg5.js +9 -0
  55. package/dist/viewer/assets/useLocalStorage-BWwvt0nG.js +1 -0
  56. package/dist/viewer/assets/useLocalStorage-BlZWItfY.js +1 -0
  57. package/dist/viewer/assets/useLocalStorage-CQO0VGQ6.js +1 -0
  58. package/dist/viewer/assets/useNavigate-9J1ZzN1J.js +1 -0
  59. package/dist/viewer/assets/useNavigate-V8wjv5GQ.js +1 -0
  60. package/dist/viewer/assets/useObjectLookups-BAqe9wvu.js +14 -0
  61. package/dist/viewer/assets/useObjectLookups-C65zm-bM.js +14 -0
  62. package/dist/viewer/assets/{useObjectLookups-Cx6A7PGj.js → useObjectLookups-CslM2vSA.js} +3 -3
  63. package/dist/viewer/assets/{validIcons-TcFXfn-E.js → validIcons-Dqgxgpv-.js} +1 -1
  64. package/dist/viewer/assets/validIcons-UmfwFGTT.js +1 -0
  65. package/dist/viewer/index.html +10 -8
  66. package/dist/viewer/mark.svg +17 -0
  67. package/package.json +8 -7
  68. package/dist/viewer/assets/AppDetailPage-BJrg5Wsz.js +0 -1
  69. package/dist/viewer/assets/BootstrapContext-D3lhzWap.js +0 -1
  70. package/dist/viewer/assets/ConfigurationPage-D8I8rWjF.js +0 -1
  71. package/dist/viewer/assets/CredentialsContext-BLZX6RwF.js +0 -1
  72. package/dist/viewer/assets/SandboxPage-ByixszHi.js +0 -1
  73. package/dist/viewer/assets/SidebarContext-Byo6PYOF.js +0 -1
  74. package/dist/viewer/assets/VersionsPage-BKul6bXI.js +0 -1
  75. package/dist/viewer/assets/index-BoSQQwMQ.css +0 -2
  76. package/dist/viewer/assets/index-Bomh3C3X.js +0 -5
  77. /package/dist/viewer/assets/{fa-pro-icons-CIQGXozY.js → fa-pro-icons-CiTC899m.js} +0 -0
package/dist/index.js CHANGED
@@ -112,24 +112,28 @@ import { Box as Box2, Text as Text2 } from "ink";
112
112
  import { Box, Text } from "ink";
113
113
  import { jsx } from "react/jsx-runtime";
114
114
  var LOGO_LINES = [
115
- " @@@ ",
116
- " @@@@@ @@@@@@@ ",
117
- " @@@@ @@@@@@@@@@ ",
118
- " @@@@ @@@@@ ",
119
- " @@@ @@@@ @@@@@ @@@@@@@ ",
120
- " @@@@ @@@@ @@@@ @@@@@@@@@@@@@",
121
- " @@@@ @@@@ @@@@@",
122
- " @@@@ @@@ @@@@ @@@",
123
- " @@@@ @@@@@@ ",
124
- " @@@@@@ @@@@ ",
125
- " @@@ @@@@@ @@@ @@@@ ",
126
- " @@@@@ @@@@ @@@@ ",
127
- " @@@@@@@@@@@@@ @@@ @@@ @@@ ",
128
- " @@@@@@@@ @@@@ @@@ @@@@ ",
129
- " @@@@@ @@@@ ",
130
- " @@@@@@@@@@@ @@@@ ",
131
- " @@@@@@@ @@@@@ ",
132
- " @@@@ "
115
+ " :::::::: ",
116
+ " :::::: ::::::::::: ",
117
+ " :::::: :::::::::: ",
118
+ " :::::: :::::: ::::::: ",
119
+ " ::::: :::::: ::::::::::::::::: ",
120
+ " ::::: ::::: ::::::::::::::::::: ",
121
+ " ::::: ::::: :::::: ::::: ",
122
+ ":::: ::::: ::: ::::: :: ",
123
+ "::::: :::::: :::::::::: ",
124
+ "::::: :::::: ::::::::::: ",
125
+ " ::::: ::: ::::::: ",
126
+ " ::::::: ::: ::::: ",
127
+ " ::::::::::: :::::: :::::",
128
+ " :::::::::: :::::: :::::",
129
+ " :: ::::: ::: ::::: ::::",
130
+ " ::::: :::::: ::::: ::::: ::: ",
131
+ " ::::::::::::::::::: ::::: ::::: ",
132
+ " ::::::::::::::::: ::::: ::::: ",
133
+ " ::::::::: :::::: :::::: ",
134
+ " :::::::::: :::::: ",
135
+ " ::::::::::: :::::: ",
136
+ " :::::::: "
133
137
  ];
134
138
  var Logo = () => /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: LOGO_LINES.map((line, i) => /* @__PURE__ */ jsx(Text, { color: "cyan", children: line }, i)) });
135
139
 
@@ -137,10 +141,7 @@ var Logo = () => /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: L
137
141
  import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
138
142
  var AppHeader = ({ marginBottom = 0 }) => /* @__PURE__ */ jsxs(Fragment, { children: [
139
143
  /* @__PURE__ */ jsx2(Logo, {}),
140
- /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", marginTop: 1, marginBottom, children: [
141
- /* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "Kizen App Builder" }),
142
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2500".repeat(24) })
143
- ] })
144
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, marginBottom, children: /* @__PURE__ */ jsx2(Text2, { bold: true, color: "cyan", children: "Kizen App Development Toolkit" }) })
144
145
  ] });
145
146
 
146
147
  // src/ui/Spinner.tsx
@@ -265,13 +266,13 @@ import { useCallback as useCallback3, useEffect as useEffect4, useRef, useState
265
266
  import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
266
267
  import { watch } from "fs";
267
268
  import { createServer } from "http";
268
- import { join as join8 } from "path";
269
+ import { join as join9 } from "path";
269
270
  import { WebSocketServer } from "ws";
270
271
 
271
272
  // src/server/requestHandler.ts
272
273
  import { createReadStream } from "fs";
273
- import { access, readFile as readFile4 } from "fs/promises";
274
- import { dirname as dirname2, extname, join as join6 } from "path";
274
+ import { access as access2, readFile as readFile4 } from "fs/promises";
275
+ import { dirname as dirname2, extname, join as join7 } from "path";
275
276
  import { fileURLToPath } from "url";
276
277
 
277
278
  // src/lib/proxyCache.ts
@@ -432,6 +433,376 @@ async function saveConfig(outputDir, config) {
432
433
  await writeFile3(join5(outputDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
433
434
  }
434
435
 
436
+ // src/server/pythonExecutor.ts
437
+ import { spawn, execFileSync } from "child_process";
438
+ import { writeFile as writeFile4, rm, mkdtemp, access } from "fs/promises";
439
+ import { join as join6 } from "path";
440
+ import { tmpdir } from "os";
441
+ var WRAPPER_SCRIPT = `
442
+ import sys, json, os, traceback
443
+
444
+ # --- Inputs proxy (attribute access) ---
445
+ class _InputsProxy:
446
+ def __init__(self, data):
447
+ object.__setattr__(self, '_data', data)
448
+
449
+ def __getattr__(self, name):
450
+ if name.startswith('_'):
451
+ raise AttributeError(name)
452
+ return self._data.get(name)
453
+
454
+ def __repr__(self):
455
+ return repr(self._data)
456
+
457
+ def __str__(self):
458
+ return str(self._data)
459
+
460
+ def __bool__(self):
461
+ return bool(self._data)
462
+
463
+ inputs = _InputsProxy(json.loads(os.environ.get('__KIZEN_INPUTS__', '{}')))
464
+
465
+ # --- Secrets ---
466
+ secrets = json.loads(os.environ.get('__KIZEN_SECRETS__', '{}'))
467
+
468
+ # --- Outputs proxy ---
469
+ class _OutputsProxy:
470
+ def __init__(self):
471
+ object.__setattr__(self, '_values', {})
472
+ object.__setattr__(self, '_logs', [])
473
+
474
+ def __setattr__(self, name, value):
475
+ if name.startswith('_'):
476
+ object.__setattr__(self, name, value)
477
+ else:
478
+ self._values[name] = value
479
+
480
+ def __getattr__(self, name):
481
+ if name.startswith('_'):
482
+ raise AttributeError(name)
483
+ return self._values.get(name)
484
+
485
+ def log(self, message):
486
+ self._logs.append(str(message))
487
+
488
+ outputs = _OutputsProxy()
489
+
490
+ # --- Execute user script ---
491
+ try:
492
+ exec(open(os.environ['__KIZEN_SCRIPT_PATH__'], encoding='utf-8').read())
493
+ result = {
494
+ 'success': True,
495
+ 'outputValues': outputs._values,
496
+ 'logs': outputs._logs,
497
+ 'error': None,
498
+ }
499
+ except Exception:
500
+ # Build a clean traceback that hides the wrapper internals.
501
+ # Keep only frames from the user script (shown as File "<string>")
502
+ # and re-label them with "script.py" for clarity.
503
+ exc_type, exc_value, exc_tb = sys.exc_info()
504
+ entries = traceback.extract_tb(exc_tb)
505
+ user_entries = [e for e in entries if e.filename != __file__]
506
+ for e in user_entries:
507
+ if e.filename == '<string>':
508
+ e.filename = 'script.py'
509
+ lines = ['Traceback (most recent call last):\\n']
510
+ lines += traceback.format_list(user_entries)
511
+ lines += traceback.format_exception_only(exc_type, exc_value)
512
+ result = {
513
+ 'success': False,
514
+ 'outputValues': outputs._values,
515
+ 'logs': outputs._logs,
516
+ 'error': ''.join(lines),
517
+ }
518
+
519
+ # Write results between markers so we can extract them reliably
520
+ # even if the user script wrote to stdout
521
+ sys.stdout.write('\\n__KIZEN_RESULT_START__\\n')
522
+ sys.stdout.write(json.dumps(result, ensure_ascii=False))
523
+ sys.stdout.write('\\n__KIZEN_RESULT_END__\\n')
524
+ sys.stdout.flush()
525
+ `.trim();
526
+ var DEFAULT_TIMEOUT = 3e4;
527
+ var VENV_DIR = join6(process.cwd(), ".kizenapp", "venv");
528
+ var VENV_PACKAGES = ["requests"];
529
+ var venvReady = null;
530
+ function resolvePythonBinary(runtime) {
531
+ const match = /^python-(\d+\.\d+)$/.exec(runtime);
532
+ if (match?.[1]) {
533
+ return `python${match[1]}`;
534
+ }
535
+ return "python3";
536
+ }
537
+ function ensureVenv(runtime) {
538
+ if (!venvReady) {
539
+ venvReady = (async () => {
540
+ const venvPython = join6(VENV_DIR, "bin", "python");
541
+ try {
542
+ await access(venvPython);
543
+ } catch {
544
+ const systemPython = resolvePythonBinary(runtime);
545
+ execFileSync(systemPython, ["-m", "venv", VENV_DIR], { stdio: "ignore" });
546
+ }
547
+ execFileSync(venvPython, ["-m", "pip", "install", "-q", ...VENV_PACKAGES], {
548
+ stdio: "ignore"
549
+ });
550
+ return venvPython;
551
+ })();
552
+ }
553
+ return venvReady;
554
+ }
555
+ function parseResult(stdout) {
556
+ const startMarker = "__KIZEN_RESULT_START__";
557
+ const endMarker = "__KIZEN_RESULT_END__";
558
+ const startIdx = stdout.indexOf(startMarker);
559
+ const endIdx = stdout.indexOf(endMarker);
560
+ if (startIdx === -1 || endIdx === -1) {
561
+ return { userStdout: stdout, result: null };
562
+ }
563
+ const userStdout = stdout.slice(0, startIdx).trimEnd();
564
+ const jsonStr = stdout.slice(startIdx + startMarker.length, endIdx).trim();
565
+ try {
566
+ const result = JSON.parse(jsonStr);
567
+ return { userStdout, result };
568
+ } catch {
569
+ return { userStdout: stdout, result: null };
570
+ }
571
+ }
572
+ async function executePythonStep(params) {
573
+ const timeout = params.timeout ?? DEFAULT_TIMEOUT;
574
+ const pythonBin = await ensureVenv(params.scriptRuntime);
575
+ const start = Date.now();
576
+ let tmpDir;
577
+ try {
578
+ tmpDir = await mkdtemp(join6(tmpdir(), "kizen-step-"));
579
+ const wrapperPath = join6(tmpDir, "wrapper.py");
580
+ const scriptPath = join6(tmpDir, "script.py");
581
+ await Promise.all([
582
+ writeFile4(wrapperPath, WRAPPER_SCRIPT, "utf-8"),
583
+ writeFile4(scriptPath, params.script, "utf-8")
584
+ ]);
585
+ const env = {
586
+ ...process.env,
587
+ __KIZEN_INPUTS__: JSON.stringify(params.inputs),
588
+ __KIZEN_SECRETS__: JSON.stringify(params.secrets),
589
+ __KIZEN_SCRIPT_PATH__: scriptPath
590
+ };
591
+ const { exitCode, stdout, stderr } = await new Promise((resolve) => {
592
+ const stdoutChunks = [];
593
+ const stderrChunks = [];
594
+ let settled = false;
595
+ const proc = spawn(pythonBin, [wrapperPath], {
596
+ env,
597
+ cwd: tmpDir,
598
+ stdio: ["ignore", "pipe", "pipe"]
599
+ });
600
+ const timer = setTimeout(() => {
601
+ proc.kill("SIGKILL");
602
+ }, timeout);
603
+ proc.stdout.on("data", (chunk) => {
604
+ stdoutChunks.push(chunk);
605
+ });
606
+ proc.stderr.on("data", (chunk) => {
607
+ stderrChunks.push(chunk);
608
+ });
609
+ const finish = (code) => {
610
+ if (settled) {
611
+ return;
612
+ }
613
+ settled = true;
614
+ clearTimeout(timer);
615
+ resolve({
616
+ exitCode: code ?? 1,
617
+ stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
618
+ stderr: Buffer.concat(stderrChunks).toString("utf-8")
619
+ });
620
+ };
621
+ proc.on("close", finish);
622
+ proc.on("error", (err) => {
623
+ if (settled) {
624
+ return;
625
+ }
626
+ settled = true;
627
+ clearTimeout(timer);
628
+ resolve({
629
+ exitCode: 1,
630
+ stdout: "",
631
+ stderr: err.message
632
+ });
633
+ });
634
+ });
635
+ const durationMs = Date.now() - start;
636
+ const { userStdout, result } = parseResult(stdout);
637
+ if (result) {
638
+ const base2 = {
639
+ success: result.success,
640
+ outputValues: result.outputValues,
641
+ logs: result.logs,
642
+ stdout: userStdout,
643
+ stderr,
644
+ exitCode,
645
+ durationMs
646
+ };
647
+ if (result.error !== null) {
648
+ base2.error = result.error;
649
+ }
650
+ return base2;
651
+ }
652
+ const base = {
653
+ success: false,
654
+ outputValues: {},
655
+ logs: [],
656
+ stdout: userStdout,
657
+ stderr,
658
+ exitCode,
659
+ durationMs
660
+ };
661
+ if (exitCode !== 0) {
662
+ base.error = `Process exited with code ${String(exitCode)}`;
663
+ }
664
+ return base;
665
+ } finally {
666
+ if (tmpDir) {
667
+ void rm(tmpDir, { recursive: true, force: true });
668
+ }
669
+ }
670
+ }
671
+
672
+ // src/server/remoteCodeRunner.ts
673
+ var BASE_URLS = {
674
+ go: "https://app.go.kizen.com/api",
675
+ fmo: "https://app.fmo.kizen.com/api",
676
+ staging: "https://staging.kizen.dev/api",
677
+ integration: "https://integration.kizen.dev/api",
678
+ test1: "https://test1.kizen.dev/api"
679
+ };
680
+ var SUPPORTED_RUNTIMES = ["python-3-13", "python-3-12"];
681
+ var DEFAULT_RUNTIME = "python-3-12";
682
+ var DATA_TYPE_TO_KIZEN = {
683
+ text: "s",
684
+ string: "s",
685
+ number: "n",
686
+ integer: "n",
687
+ boolean: "b",
688
+ date: "d",
689
+ datetime: "dt",
690
+ phone: "p",
691
+ phonenumber: "p",
692
+ uuid: "u",
693
+ file: "f",
694
+ list: "l"
695
+ };
696
+ function toKizenType(dataType) {
697
+ return DATA_TYPE_TO_KIZEN[dataType.toLowerCase()] ?? "s";
698
+ }
699
+ function resolveRuntime(runtime) {
700
+ if (SUPPORTED_RUNTIMES.includes(runtime)) {
701
+ return runtime;
702
+ }
703
+ return DEFAULT_RUNTIME;
704
+ }
705
+ function encodeInputs(inputs, inputTypes) {
706
+ const keys = Object.keys(inputs);
707
+ if (keys.length === 0) {
708
+ return null;
709
+ }
710
+ const encoded = {};
711
+ for (const key of keys) {
712
+ encoded[key] = {
713
+ t: toKizenType(inputTypes[key] ?? "string"),
714
+ v: inputs[key]
715
+ };
716
+ }
717
+ return encoded;
718
+ }
719
+ function encodeOutputTypes(outputTypes) {
720
+ const keys = Object.keys(outputTypes);
721
+ if (keys.length === 0) {
722
+ return null;
723
+ }
724
+ const encoded = {};
725
+ for (const key of keys) {
726
+ encoded[key] = toKizenType(outputTypes[key] ?? "string");
727
+ }
728
+ return encoded;
729
+ }
730
+ function decodeOutputValues(values) {
731
+ if (!values) {
732
+ return {};
733
+ }
734
+ const decoded = {};
735
+ for (const [key, encoded] of Object.entries(values)) {
736
+ decoded[key] = encoded.v;
737
+ }
738
+ return decoded;
739
+ }
740
+ async function executeRemoteStep(params, credentials) {
741
+ const baseUrl = BASE_URLS[credentials.environment];
742
+ const url = `${baseUrl}/coderunner/run`;
743
+ const start = Date.now();
744
+ const body = {
745
+ user_script: params.script,
746
+ runtime: resolveRuntime(params.scriptRuntime),
747
+ secrets: params.secretNames.length > 0 ? params.secretNames : void 0,
748
+ inputs: encodeInputs(params.inputs, params.inputTypes),
749
+ output_types: encodeOutputTypes(params.outputTypes)
750
+ };
751
+ try {
752
+ const response = await fetch(url, {
753
+ method: "POST",
754
+ headers: {
755
+ "Content-Type": "application/json",
756
+ "X-API-KEY": credentials.apiKey,
757
+ "X-USER-ID": credentials.userId,
758
+ "X-BUSINESS-ID": credentials.businessId
759
+ },
760
+ body: JSON.stringify(body)
761
+ });
762
+ const durationMs = Date.now() - start;
763
+ if (!response.ok) {
764
+ const text = await response.text();
765
+ return {
766
+ success: false,
767
+ outputValues: {},
768
+ logs: [],
769
+ stdout: "",
770
+ stderr: "",
771
+ error: `Remote runner returned ${String(response.status)}: ${text}`,
772
+ exitCode: 1,
773
+ durationMs
774
+ };
775
+ }
776
+ const data = await response.json();
777
+ const hasError = data.error !== null;
778
+ const result = {
779
+ success: !hasError,
780
+ outputValues: decodeOutputValues(data.values),
781
+ logs: data.logs ?? [],
782
+ stdout: "",
783
+ stderr: "",
784
+ exitCode: hasError ? 1 : 0,
785
+ durationMs: data.duration_ms ?? durationMs
786
+ };
787
+ if (hasError && data.error) {
788
+ result.error = data.error.detail ? `${data.error.error}
789
+ ${data.error.detail}` : data.error.error;
790
+ }
791
+ return result;
792
+ } catch (err) {
793
+ return {
794
+ success: false,
795
+ outputValues: {},
796
+ logs: [],
797
+ stdout: "",
798
+ stderr: "",
799
+ error: `Remote execution failed: ${err instanceof Error ? err.message : String(err)}`,
800
+ exitCode: 1,
801
+ durationMs: Date.now() - start
802
+ };
803
+ }
804
+ }
805
+
435
806
  // src/server/requestHandler.ts
436
807
  function proxyLogEntryToString(entry) {
437
808
  if (entry.kind === "info") {
@@ -442,17 +813,17 @@ function proxyLogEntryToString(entry) {
442
813
  }
443
814
  function getViewerPath() {
444
815
  const filename = fileURLToPath(import.meta.url);
445
- return join6(dirname2(filename), "viewer");
816
+ return join7(dirname2(filename), "viewer");
446
817
  }
447
818
  async function fileExists(filePath) {
448
819
  try {
449
- await access(filePath);
820
+ await access2(filePath);
450
821
  return true;
451
822
  } catch {
452
823
  return false;
453
824
  }
454
825
  }
455
- function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast, onProxyCacheChange) {
826
+ function createRequestHandler(viewerPath, createServerLog, createProxyLog, credentialsRef, activeProfileRef, outputDir, broadcast, onProxyCacheChange, lastPathRef) {
456
827
  const proxyCache = createProxyCache({ onChange: onProxyCacheChange });
457
828
  return (req, res) => {
458
829
  void (async () => {
@@ -493,7 +864,7 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
493
864
  return;
494
865
  }
495
866
  if (url === "/api/bundle") {
496
- const bundlePath = join6(process.cwd(), ".kizenapp", "bundle.json");
867
+ const bundlePath = join7(process.cwd(), ".kizenapp", "bundle.json");
497
868
  try {
498
869
  const content = await readFile4(bundlePath, "utf-8");
499
870
  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
@@ -504,6 +875,66 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
504
875
  }
505
876
  return;
506
877
  }
878
+ if (url === "/api/last-path" && req.method === "POST") {
879
+ const chunks = [];
880
+ for await (const chunk of req) {
881
+ chunks.push(chunk);
882
+ }
883
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf-8") || "{}");
884
+ if (typeof body.path === "string") {
885
+ lastPathRef.current = body.path;
886
+ const currentConfig = await loadConfig(outputDir);
887
+ await saveConfig(outputDir, { ...currentConfig, lastPath: body.path });
888
+ }
889
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
890
+ res.end('{"ok":true}');
891
+ return;
892
+ }
893
+ if (url === "/api/execute-step" && req.method === "POST") {
894
+ const chunks = [];
895
+ for await (const chunk of req) {
896
+ chunks.push(chunk);
897
+ }
898
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf-8") || "{}");
899
+ if (typeof body.script !== "string") {
900
+ res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
901
+ res.end(JSON.stringify({ error: "Missing required field: script" }));
902
+ return;
903
+ }
904
+ let result;
905
+ if (body.mode === "remote") {
906
+ if (!credentialsRef.current) {
907
+ res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
908
+ res.end(JSON.stringify({ error: "Credentials are required for remote execution" }));
909
+ return;
910
+ }
911
+ result = await executeRemoteStep(
912
+ {
913
+ script: body.script,
914
+ scriptRuntime: body.scriptRuntime ?? "python-3-12",
915
+ inputs: body.inputs ?? {},
916
+ inputTypes: body.inputTypes ?? {},
917
+ outputTypes: body.outputTypes ?? {},
918
+ secretNames: body.secretNames ?? []
919
+ },
920
+ credentialsRef.current
921
+ );
922
+ } else {
923
+ const stepParams = {
924
+ script: body.script,
925
+ scriptRuntime: body.scriptRuntime ?? "python-3.9",
926
+ inputs: body.inputs ?? {},
927
+ secrets: body.secrets ?? {}
928
+ };
929
+ if (typeof body.timeout === "number") {
930
+ stepParams.timeout = body.timeout;
931
+ }
932
+ result = await executePythonStep(stepParams);
933
+ }
934
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
935
+ res.end(JSON.stringify(result));
936
+ return;
937
+ }
507
938
  if (url === "/api/proxy-cache/clear") {
508
939
  proxyCache.clear();
509
940
  createProxyLog({ kind: "info", message: "Proxy cache cleared" });
@@ -568,8 +999,8 @@ function createRequestHandler(viewerPath, createServerLog, createProxyLog, crede
568
999
  return;
569
1000
  }
570
1001
  const rawPath = url === "/" ? "/index.html" : url;
571
- const filePath = join6(viewerPath, rawPath);
572
- const resolvedPath = await fileExists(filePath) ? filePath : join6(viewerPath, "index.html");
1002
+ const filePath = join7(viewerPath, rawPath);
1003
+ const resolvedPath = await fileExists(filePath) ? filePath : join7(viewerPath, "index.html");
573
1004
  const ext = extname(resolvedPath);
574
1005
  const mimeType = MIME_TYPES[ext] ?? "application/octet-stream";
575
1006
  res.writeHead(200, { "Content-Type": mimeType });
@@ -603,7 +1034,7 @@ import { launch } from "chrome-launcher";
603
1034
  import { createRequire } from "module";
604
1035
  import { mkdirSync } from "fs";
605
1036
  import { readFile as readFile5 } from "fs/promises";
606
- import { dirname as dirname3, join as join7 } from "path";
1037
+ import { dirname as dirname3, join as join8 } from "path";
607
1038
  import { fileURLToPath as fileURLToPath2 } from "url";
608
1039
  var CDP = createRequire(import.meta.url)("chrome-remote-interface");
609
1040
  var CSP_HEADERS = /* @__PURE__ */ new Set([
@@ -682,7 +1113,7 @@ async function serializeArgAsync(client, arg) {
682
1113
  }
683
1114
  async function installDockIcon(client) {
684
1115
  try {
685
- const iconPath = join7(dirname3(fileURLToPath2(import.meta.url)), "viewer", "icon.png");
1116
+ const iconPath = join8(dirname3(fileURLToPath2(import.meta.url)), "viewer", "icon.png");
686
1117
  const icon = await readFile5(iconPath);
687
1118
  await client.Browser.setDockTile({ image: icon.toString("base64") });
688
1119
  } catch {
@@ -690,10 +1121,11 @@ async function installDockIcon(client) {
690
1121
  }
691
1122
  async function launchChromeViewer(opts) {
692
1123
  mkdirSync(opts.userDataDir, { recursive: true });
1124
+ const baseUrl = `http://localhost:${String(opts.port)}`;
693
1125
  const chrome = await launch({
694
- startingUrl: `http://localhost:${String(opts.port)}`,
1126
+ startingUrl: baseUrl,
695
1127
  chromeFlags: [
696
- `--app=http://localhost:${String(opts.port)}`,
1128
+ `--app=${baseUrl}`,
697
1129
  "--no-first-run",
698
1130
  "--no-default-browser-check",
699
1131
  "--disable-extensions"
@@ -709,6 +1141,9 @@ async function launchChromeViewer(opts) {
709
1141
  await new Promise((r) => setTimeout(r, 300));
710
1142
  try {
711
1143
  client = await CDP({ port: cdpPort });
1144
+ if (opts.path) {
1145
+ void client.send("Page.navigate", { url: `${baseUrl}${opts.path}` });
1146
+ }
712
1147
  break;
713
1148
  } catch {
714
1149
  if (attempt === 9) {
@@ -1203,10 +1638,12 @@ var DevUI = ({
1203
1638
  outputDir,
1204
1639
  credentials: initialCredentials,
1205
1640
  credentialMode: initialCredentialMode,
1206
- activeCredentialProfile: initialActiveProfile
1641
+ activeCredentialProfile: initialActiveProfile,
1642
+ lastPath: initialLastPath
1207
1643
  }) => {
1208
1644
  const credentialsRef = useRef(initialCredentials);
1209
1645
  const activeProfileRef = useRef(initialActiveProfile);
1646
+ const lastPathRef = useRef(initialLastPath);
1210
1647
  const [credMode, setCredMode] = useState5("main");
1211
1648
  const [credentialMode, setCredentialMode] = useState5(
1212
1649
  initialCredentialMode
@@ -1248,8 +1685,9 @@ var DevUI = ({
1248
1685
  chromeHandleRef.current = null;
1249
1686
  void launchChromeViewer({
1250
1687
  port,
1251
- userDataDir: join8(outputDir, ".chrome"),
1252
- broadcast
1688
+ userDataDir: join9(outputDir, ".chrome"),
1689
+ broadcast,
1690
+ ...lastPathRef.current !== void 0 && { path: lastPathRef.current }
1253
1691
  }).then((handle) => {
1254
1692
  chromeHandleRef.current = handle;
1255
1693
  process.on("exit", () => {
@@ -1370,7 +1808,7 @@ var DevUI = ({
1370
1808
  }, [pluginDir, triggerBuild, createBuildLog]);
1371
1809
  useEffect4(() => {
1372
1810
  const viewerPath = getViewerPath();
1373
- void fileExists(join8(viewerPath, "index.html")).then((viewerBuilt) => {
1811
+ void fileExists(join9(viewerPath, "index.html")).then((viewerBuilt) => {
1374
1812
  if (!viewerBuilt) {
1375
1813
  setErrorMessage("Viewer not built. Run 'pnpm build:viewer' first.");
1376
1814
  setStatus("error");
@@ -1384,7 +1822,8 @@ var DevUI = ({
1384
1822
  activeProfileRef,
1385
1823
  outputDir,
1386
1824
  broadcast,
1387
- setProxyCacheSize
1825
+ setProxyCacheSize,
1826
+ lastPathRef
1388
1827
  );
1389
1828
  const server = createServer(handler);
1390
1829
  const wss = new WebSocketServer({ server });
@@ -1572,6 +2011,7 @@ function devCommand(program2) {
1572
2011
  let credentials = null;
1573
2012
  let credentialMode;
1574
2013
  let activeCredentialProfile;
2014
+ const { lastPath } = await loadConfig(outputDir);
1575
2015
  if (options.credentials) {
1576
2016
  credentials = await loadCredentialsFromFile(options.credentials);
1577
2017
  } else {
@@ -1614,7 +2054,8 @@ function devCommand(program2) {
1614
2054
  outputDir,
1615
2055
  credentials,
1616
2056
  ...credentialMode !== void 0 && { credentialMode },
1617
- ...activeCredentialProfile !== void 0 && { activeCredentialProfile }
2057
+ ...activeCredentialProfile !== void 0 && { activeCredentialProfile },
2058
+ ...lastPath !== void 0 && { lastPath }
1618
2059
  }),
1619
2060
  { exitOnCtrlC: false }
1620
2061
  );