@kortix/sandbox 0.4.1

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 (246) hide show
  1. package/config/customize.sh +143 -0
  2. package/config/kortix-env-setup.sh +25 -0
  3. package/kortix-master/package.json +22 -0
  4. package/kortix-master/src/config.ts +22 -0
  5. package/kortix-master/src/index.ts +44 -0
  6. package/kortix-master/src/routes/env.ts +65 -0
  7. package/kortix-master/src/routes/proxy.ts +108 -0
  8. package/kortix-master/src/routes/update.ts +185 -0
  9. package/kortix-master/src/services/proxy.ts +43 -0
  10. package/kortix-master/src/services/secret-store.ts +156 -0
  11. package/kortix-master/tsconfig.json +14 -0
  12. package/opencode/agents/kortix-browser.md +142 -0
  13. package/opencode/agents/kortix-build.md +62 -0
  14. package/opencode/agents/kortix-explore.md +66 -0
  15. package/opencode/agents/kortix-image-gen.md +33 -0
  16. package/opencode/agents/kortix-main.md +450 -0
  17. package/opencode/agents/kortix-plan.md +100 -0
  18. package/opencode/agents/kortix-research.md +84 -0
  19. package/opencode/agents/kortix-sheets.md +61 -0
  20. package/opencode/agents/kortix-slides.md +64 -0
  21. package/opencode/agents/kortix-web-dev.md +572 -0
  22. package/opencode/commands/email.md +36 -0
  23. package/opencode/commands/init.md +43 -0
  24. package/opencode/commands/journal.md +44 -0
  25. package/opencode/commands/memory-init.md +81 -0
  26. package/opencode/commands/memory-search.md +50 -0
  27. package/opencode/commands/memory-status.md +56 -0
  28. package/opencode/commands/research.md +36 -0
  29. package/opencode/commands/search.md +38 -0
  30. package/opencode/commands/slides.md +32 -0
  31. package/opencode/commands/spreadsheet.md +30 -0
  32. package/opencode/memory.json +37 -0
  33. package/opencode/ocx.jsonc +10 -0
  34. package/opencode/opencode.jsonc +103 -0
  35. package/opencode/package.json +25 -0
  36. package/opencode/patches/apply.sh +19 -0
  37. package/opencode/patches/opencode-pty-spawn.txt +49 -0
  38. package/opencode/plugin/background-agents.ts.disabled +483 -0
  39. package/opencode/plugin/kdco-primitives/get-project-id.ts +172 -0
  40. package/opencode/plugin/kdco-primitives/index.ts +26 -0
  41. package/opencode/plugin/kdco-primitives/log-warn.ts +51 -0
  42. package/opencode/plugin/kdco-primitives/mutex.ts +122 -0
  43. package/opencode/plugin/kdco-primitives/shell.ts +138 -0
  44. package/opencode/plugin/kdco-primitives/temp.ts +36 -0
  45. package/opencode/plugin/kdco-primitives/terminal-detect.ts +34 -0
  46. package/opencode/plugin/kdco-primitives/types.ts +13 -0
  47. package/opencode/plugin/kdco-primitives/with-timeout.ts +84 -0
  48. package/opencode/plugin/memory.ts +306 -0
  49. package/opencode/plugin/worktree/state.ts +412 -0
  50. package/opencode/plugin/worktree/terminal.ts +1002 -0
  51. package/opencode/plugin/worktree.ts +861 -0
  52. package/opencode/skills/KORTIX-browser/SKILL.md +478 -0
  53. package/opencode/skills/KORTIX-cron-triggers/SKILL.md +173 -0
  54. package/opencode/skills/KORTIX-deep-research/SKILL.md +278 -0
  55. package/opencode/skills/KORTIX-docx/SKILL.md +398 -0
  56. package/opencode/skills/KORTIX-docx/scripts/__init__.py +1 -0
  57. package/opencode/skills/KORTIX-docx/scripts/accept_changes.py +104 -0
  58. package/opencode/skills/KORTIX-docx/scripts/comment.py +244 -0
  59. package/opencode/skills/KORTIX-docx/scripts/office/helpers/__init__.py +0 -0
  60. package/opencode/skills/KORTIX-docx/scripts/office/helpers/merge_runs.py +199 -0
  61. package/opencode/skills/KORTIX-docx/scripts/office/helpers/simplify_redlines.py +197 -0
  62. package/opencode/skills/KORTIX-docx/scripts/office/pack.py +159 -0
  63. package/opencode/skills/KORTIX-docx/scripts/office/soffice.py +183 -0
  64. package/opencode/skills/KORTIX-docx/scripts/office/unpack.py +132 -0
  65. package/opencode/skills/KORTIX-docx/scripts/office/validate.py +111 -0
  66. package/opencode/skills/KORTIX-docx/scripts/office/validators/__init__.py +15 -0
  67. package/opencode/skills/KORTIX-docx/scripts/office/validators/base.py +847 -0
  68. package/opencode/skills/KORTIX-docx/scripts/office/validators/docx.py +446 -0
  69. package/opencode/skills/KORTIX-docx/scripts/office/validators/pptx.py +275 -0
  70. package/opencode/skills/KORTIX-docx/scripts/office/validators/redlining.py +247 -0
  71. package/opencode/skills/KORTIX-docx/scripts/render_docx.py +179 -0
  72. package/opencode/skills/KORTIX-docx/scripts/templates/comments.xml +3 -0
  73. package/opencode/skills/KORTIX-docx/scripts/templates/commentsExtended.xml +3 -0
  74. package/opencode/skills/KORTIX-docx/scripts/templates/commentsExtensible.xml +3 -0
  75. package/opencode/skills/KORTIX-docx/scripts/templates/commentsIds.xml +3 -0
  76. package/opencode/skills/KORTIX-docx/scripts/templates/people.xml +3 -0
  77. package/opencode/skills/KORTIX-domain-research/SKILL.md +96 -0
  78. package/opencode/skills/KORTIX-domain-research/scripts/domain-lookup.py +810 -0
  79. package/opencode/skills/KORTIX-elevenlabs/SKILL.md +230 -0
  80. package/opencode/skills/KORTIX-elevenlabs/scripts/tts.py +389 -0
  81. package/opencode/skills/KORTIX-email/SKILL.md +145 -0
  82. package/opencode/skills/KORTIX-legal-writer/SKILL.md +409 -0
  83. package/opencode/skills/KORTIX-legal-writer/references/bluebook.md +152 -0
  84. package/opencode/skills/KORTIX-legal-writer/references/document-types.md +416 -0
  85. package/opencode/skills/KORTIX-legal-writer/scripts/courtlistener.py +291 -0
  86. package/opencode/skills/KORTIX-legal-writer/scripts/ecfr_lookup.py +299 -0
  87. package/opencode/skills/KORTIX-legal-writer/scripts/verify-legal.py +507 -0
  88. package/opencode/skills/KORTIX-logo-creator/SKILL.md +293 -0
  89. package/opencode/skills/KORTIX-logo-creator/references/prompt-patterns.md +134 -0
  90. package/opencode/skills/KORTIX-logo-creator/scripts/compose_logo.py +406 -0
  91. package/opencode/skills/KORTIX-logo-creator/scripts/create_logo_sheet.py +258 -0
  92. package/opencode/skills/KORTIX-logo-creator/scripts/remove_bg.py +96 -0
  93. package/opencode/skills/KORTIX-memory/SKILL.md +261 -0
  94. package/opencode/skills/KORTIX-memory/scripts/export-sessions.py +409 -0
  95. package/opencode/skills/KORTIX-paper-creator/SKILL.md +549 -0
  96. package/opencode/skills/KORTIX-paper-creator/assets/template.tex +101 -0
  97. package/opencode/skills/KORTIX-paper-creator/scripts/compile.sh +177 -0
  98. package/opencode/skills/KORTIX-paper-creator/scripts/openalex_to_bibtex.py +220 -0
  99. package/opencode/skills/KORTIX-paper-creator/scripts/verify.sh +354 -0
  100. package/opencode/skills/KORTIX-paper-search/SKILL.md +418 -0
  101. package/opencode/skills/KORTIX-pdf/SKILL.md +232 -0
  102. package/opencode/skills/KORTIX-pdf/forms.md +36 -0
  103. package/opencode/skills/KORTIX-pdf/reference.md +105 -0
  104. package/opencode/skills/KORTIX-pdf/scripts/check_bounding_boxes.py +65 -0
  105. package/opencode/skills/KORTIX-pdf/scripts/check_fillable_fields.py +11 -0
  106. package/opencode/skills/KORTIX-pdf/scripts/convert_pdf_to_images.py +33 -0
  107. package/opencode/skills/KORTIX-pdf/scripts/create_validation_image.py +37 -0
  108. package/opencode/skills/KORTIX-pdf/scripts/extract_form_field_info.py +122 -0
  109. package/opencode/skills/KORTIX-pdf/scripts/extract_form_structure.py +115 -0
  110. package/opencode/skills/KORTIX-pdf/scripts/fill_fillable_fields.py +98 -0
  111. package/opencode/skills/KORTIX-pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  112. package/opencode/skills/KORTIX-plan/SKILL.md +228 -0
  113. package/opencode/skills/KORTIX-presentation-viewer/SKILL.md +87 -0
  114. package/opencode/skills/KORTIX-presentation-viewer/serve.ts +136 -0
  115. package/opencode/skills/KORTIX-presentation-viewer/viewer.html +559 -0
  116. package/opencode/skills/KORTIX-presentations/SKILL.md +344 -0
  117. package/opencode/skills/KORTIX-remotion/SKILL.md +56 -0
  118. package/opencode/skills/KORTIX-remotion/rules/3d.md +86 -0
  119. package/opencode/skills/KORTIX-remotion/rules/animations.md +29 -0
  120. package/opencode/skills/KORTIX-remotion/rules/assets.md +78 -0
  121. package/opencode/skills/KORTIX-remotion/rules/audio-visualization.md +198 -0
  122. package/opencode/skills/KORTIX-remotion/rules/audio.md +169 -0
  123. package/opencode/skills/KORTIX-remotion/rules/calculate-metadata.md +104 -0
  124. package/opencode/skills/KORTIX-remotion/rules/can-decode.md +75 -0
  125. package/opencode/skills/KORTIX-remotion/rules/charts.md +120 -0
  126. package/opencode/skills/KORTIX-remotion/rules/compositions.md +141 -0
  127. package/opencode/skills/KORTIX-remotion/rules/display-captions.md +184 -0
  128. package/opencode/skills/KORTIX-remotion/rules/extract-frames.md +229 -0
  129. package/opencode/skills/KORTIX-remotion/rules/ffmpeg.md +38 -0
  130. package/opencode/skills/KORTIX-remotion/rules/fonts.md +152 -0
  131. package/opencode/skills/KORTIX-remotion/rules/get-audio-duration.md +58 -0
  132. package/opencode/skills/KORTIX-remotion/rules/get-video-dimensions.md +68 -0
  133. package/opencode/skills/KORTIX-remotion/rules/get-video-duration.md +58 -0
  134. package/opencode/skills/KORTIX-remotion/rules/gifs.md +141 -0
  135. package/opencode/skills/KORTIX-remotion/rules/images.md +130 -0
  136. package/opencode/skills/KORTIX-remotion/rules/import-srt-captions.md +69 -0
  137. package/opencode/skills/KORTIX-remotion/rules/light-leaks.md +73 -0
  138. package/opencode/skills/KORTIX-remotion/rules/lottie.md +68 -0
  139. package/opencode/skills/KORTIX-remotion/rules/maps.md +401 -0
  140. package/opencode/skills/KORTIX-remotion/rules/measuring-dom-nodes.md +35 -0
  141. package/opencode/skills/KORTIX-remotion/rules/measuring-text.md +143 -0
  142. package/opencode/skills/KORTIX-remotion/rules/parameters.md +98 -0
  143. package/opencode/skills/KORTIX-remotion/rules/sequencing.md +118 -0
  144. package/opencode/skills/KORTIX-remotion/rules/subtitles.md +36 -0
  145. package/opencode/skills/KORTIX-remotion/rules/tailwind.md +11 -0
  146. package/opencode/skills/KORTIX-remotion/rules/text-animations.md +20 -0
  147. package/opencode/skills/KORTIX-remotion/rules/timing.md +179 -0
  148. package/opencode/skills/KORTIX-remotion/rules/transcribe-captions.md +70 -0
  149. package/opencode/skills/KORTIX-remotion/rules/transitions.md +197 -0
  150. package/opencode/skills/KORTIX-remotion/rules/transparent-videos.md +106 -0
  151. package/opencode/skills/KORTIX-remotion/rules/trimming.md +53 -0
  152. package/opencode/skills/KORTIX-remotion/rules/videos.md +171 -0
  153. package/opencode/skills/KORTIX-secrets/SKILL.md +280 -0
  154. package/opencode/skills/KORTIX-semantic-search/SKILL.md +213 -0
  155. package/opencode/skills/KORTIX-session-search/SKILL.md +807 -0
  156. package/opencode/skills/KORTIX-session-search/Untitled +1 -0
  157. package/opencode/skills/KORTIX-skill-creator/SKILL.md +163 -0
  158. package/opencode/skills/KORTIX-web-research/SKILL.md +69 -0
  159. package/opencode/skills/KORTIX-xlsx/LICENSE.txt +30 -0
  160. package/opencode/skills/KORTIX-xlsx/SKILL.md +549 -0
  161. package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/__init__.py +0 -0
  162. package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/merge_runs.py +199 -0
  163. package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  164. package/opencode/skills/KORTIX-xlsx/scripts/office/pack.py +159 -0
  165. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  166. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  167. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  168. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  169. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  170. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  171. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  172. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  173. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  174. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  175. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  176. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  177. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  178. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  179. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  180. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  181. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  182. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  183. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  184. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  185. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  186. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  187. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  188. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  189. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  190. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  191. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  192. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  193. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  194. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  195. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  196. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  197. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  198. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  199. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  200. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  201. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  202. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  203. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  204. package/opencode/skills/KORTIX-xlsx/scripts/office/soffice.py +183 -0
  205. package/opencode/skills/KORTIX-xlsx/scripts/office/unpack.py +132 -0
  206. package/opencode/skills/KORTIX-xlsx/scripts/office/validate.py +111 -0
  207. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/__init__.py +15 -0
  208. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/base.py +847 -0
  209. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/docx.py +446 -0
  210. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/pptx.py +275 -0
  211. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/redlining.py +247 -0
  212. package/opencode/skills/KORTIX-xlsx/scripts/recalc.py +184 -0
  213. package/opencode/tools/image-gen.ts +342 -0
  214. package/opencode/tools/image-search.ts +190 -0
  215. package/opencode/tools/memory-get.ts +168 -0
  216. package/opencode/tools/memory-search.ts +247 -0
  217. package/opencode/tools/presentation-gen.ts +723 -0
  218. package/opencode/tools/scrape-webpage.ts +115 -0
  219. package/opencode/tools/scripts/.python-version +1 -0
  220. package/opencode/tools/scripts/convert_pdf.py +184 -0
  221. package/opencode/tools/scripts/convert_pptx.py +562 -0
  222. package/opencode/tools/scripts/pyproject.toml +11 -0
  223. package/opencode/tools/scripts/uv.lock +287 -0
  224. package/opencode/tools/scripts/validate_slide.py +74 -0
  225. package/opencode/tools/show-user.ts +217 -0
  226. package/opencode/tools/tests/e2e-presentation-fix.ts +277 -0
  227. package/opencode/tools/tests/image-gen.test.ts +215 -0
  228. package/opencode/tools/tests/image-search.test.ts +125 -0
  229. package/opencode/tools/tests/memory-system-benchmark.ts +1076 -0
  230. package/opencode/tools/tests/presentation-gen.test.ts +389 -0
  231. package/opencode/tools/tests/scrape-webpage.test.ts +74 -0
  232. package/opencode/tools/tests/show-user.test.ts +241 -0
  233. package/opencode/tools/tests/video-gen.test.ts +110 -0
  234. package/opencode/tools/tests/web-search.test.ts +106 -0
  235. package/opencode/tools/video-gen.ts +200 -0
  236. package/opencode/tools/web-search.ts +153 -0
  237. package/opencode/tsconfig.json +29 -0
  238. package/package.json +36 -0
  239. package/patch-agent-browser.js +100 -0
  240. package/postinstall.sh +88 -0
  241. package/services/KORTIX-presentation-viewer/run +37 -0
  242. package/services/agent-browser-viewer/run +48 -0
  243. package/services/kortix-master/run +16 -0
  244. package/services/lss-sync/run +22 -0
  245. package/services/opencode-serve/run +25 -0
  246. package/services/opencode-web/run +21 -0
@@ -0,0 +1,143 @@
1
+ #!/bin/bash
2
+ CONFIG_DIR="/workspace"
3
+ MARKER="$CONFIG_DIR/.heyagi-customized"
4
+
5
+ # ── Always run: ensure agent-browser dirs exist, clean stale locks ──────────
6
+ mkdir -p "$CONFIG_DIR/.agent-browser" "$CONFIG_DIR/.browser-profile"
7
+ rm -f "$CONFIG_DIR/.browser-profile/SingletonLock" \
8
+ "$CONFIG_DIR/.browser-profile/SingletonCookie" \
9
+ "$CONFIG_DIR/.browser-profile/SingletonSocket" 2>/dev/null
10
+ chown -R abc:abc "$CONFIG_DIR/.agent-browser" "$CONFIG_DIR/.browser-profile"
11
+
12
+ if [ -f "$MARKER" ]; then
13
+ echo "[heyagi] Already customized, skipping."
14
+ exit 0
15
+ fi
16
+
17
+ echo "[heyagi] Applying desktop customization..."
18
+ mkdir -p "$CONFIG_DIR/.config/autostart"
19
+ mkdir -p "$CONFIG_DIR/.local/share/konsole"
20
+
21
+ # ── Symlink presentations into Desktop for easy access ─────────────────────
22
+ mkdir -p "$CONFIG_DIR/presentations"
23
+ # Create a Desktop directory for KDE and symlink presentations into it
24
+ mkdir -p "$CONFIG_DIR/Desktop"
25
+ ln -sfn "$CONFIG_DIR/presentations" "$CONFIG_DIR/Desktop/presentations"
26
+
27
+ # ── KDE Global: Breeze Dark ────────────────────────────────────────────────
28
+ cat > "$CONFIG_DIR/.config/kdeglobals" << 'EOF'
29
+ [General]
30
+ ColorScheme=BreezeDark
31
+ Name=Breeze Dark
32
+ widgetStyle=Breeze
33
+
34
+ [Icons]
35
+ Theme=breeze-dark
36
+
37
+ [KDE]
38
+ LookAndFeelPackage=org.kde.breezedark.desktop
39
+ widgetStyle=breeze
40
+ EOF
41
+
42
+ # ── KWin ────────────────────────────────────────────────────────────────────
43
+ cat > "$CONFIG_DIR/.config/kwinrc" << 'EOF'
44
+ [org.kde.kdecoration2]
45
+ theme=Breeze
46
+ library=org.kde.breeze
47
+
48
+ [Windows]
49
+ Placement=Centered
50
+
51
+ [Desktops]
52
+ Number=1
53
+ Rows=1
54
+ EOF
55
+
56
+ # ── Plasma theme ────────────────────────────────────────────────────────────
57
+ cat > "$CONFIG_DIR/.config/plasmarc" << 'EOF'
58
+ [Theme]
59
+ name=breeze-dark
60
+ EOF
61
+
62
+ # ── Konsole dark profile ───────────────────────────────────────────────────
63
+ cat > "$CONFIG_DIR/.local/share/konsole/HeyAGI.profile" << 'EOF'
64
+ [Appearance]
65
+ ColorScheme=Breeze
66
+ Font=Monospace,11,-1,5,50,0,0,0,0,0
67
+
68
+ [General]
69
+ Name=HeyAGI
70
+ Parent=FALLBACK/
71
+
72
+ [Scrolling]
73
+ HistoryMode=2
74
+ EOF
75
+
76
+ cat > "$CONFIG_DIR/.config/konsolerc" << 'EOF'
77
+ [Desktop Entry]
78
+ DefaultProfile=HeyAGI.profile
79
+
80
+ [MainWindow]
81
+ MenuBar=Disabled
82
+ ToolBarsMovable=Disabled
83
+ EOF
84
+
85
+ # ── Autostart: apply wallpaper + launcher icon after KDE session loads ──────
86
+ cat > "$CONFIG_DIR/.config/autostart/heyagi-desktop.desktop" << 'EOF'
87
+ [Desktop Entry]
88
+ Type=Application
89
+ Name=HeyAGI Desktop Setup
90
+ Exec=/usr/share/wallpapers/heyagi/apply-desktop.sh
91
+ X-KDE-autostart-phase=2
92
+ EOF
93
+
94
+ cat > /usr/share/wallpapers/heyagi/apply-desktop.sh << 'SCRIPT'
95
+ #!/bin/bash
96
+ sleep 5
97
+
98
+ plasma-apply-wallpaperimage /usr/share/wallpapers/heyagi/wallpaper.png
99
+
100
+ PLASMA_RC="$HOME/.config/plasma-org.kde.plasma.desktop-appletsrc"
101
+ ICON_PATH="/usr/share/icons/heyagi/kortix-symbol-white.svg"
102
+
103
+ if [ -f "$PLASMA_RC" ]; then
104
+ # Find the kickoff applet's [Configuration][General] section and inject icon
105
+ # Get the containment/applet IDs for kickoff
106
+ KICKOFF_SECTION=$(grep -B3 "plugin=org.kde.plasma.kickoff" "$PLASMA_RC" | grep "^\[Containments\]" | tail -1)
107
+
108
+ if [ -n "$KICKOFF_SECTION" ]; then
109
+ # Build the [Configuration][General] section name
110
+ GENERAL_SECTION="${KICKOFF_SECTION%]}][Configuration][General]"
111
+
112
+ if grep -q "$(echo "$GENERAL_SECTION" | sed 's/\[/\\[/g; s/\]/\\]/g')" "$PLASMA_RC"; then
113
+ # Section exists -- replace or add icon line
114
+ ESCAPED=$(echo "$GENERAL_SECTION" | sed 's/\[/\\[/g; s/\]/\\]/g')
115
+ if grep -A10 "$ESCAPED" "$PLASMA_RC" | grep -q "^icon="; then
116
+ sed -i "/$ESCAPED/,/^\[/{s|^icon=.*|icon=$ICON_PATH|}" "$PLASMA_RC"
117
+ else
118
+ sed -i "/$ESCAPED/a icon=$ICON_PATH" "$PLASMA_RC"
119
+ fi
120
+ else
121
+ # Section doesn't exist -- create it
122
+ CONF_SECTION="${KICKOFF_SECTION%]}][Configuration]"
123
+ ESCAPED_CONF=$(echo "$CONF_SECTION" | sed 's/\[/\\[/g; s/\]/\\]/g')
124
+ sed -i "/$ESCAPED_CONF/,/^\[/{/^\[.*\]/!b;i\\${GENERAL_SECTION}\nicon=$ICON_PATH
125
+ }" "$PLASMA_RC"
126
+ fi
127
+ fi
128
+
129
+ # Restart plasmashell to pick up icon change
130
+ kquitapp5 plasmashell 2>/dev/null
131
+ sleep 2
132
+ kstart5 plasmashell 2>/dev/null &
133
+ fi
134
+ SCRIPT
135
+ chmod +x /usr/share/wallpapers/heyagi/apply-desktop.sh
136
+
137
+ # ── Fix ownership ──────────────────────────────────────────────────────────
138
+ # Give abc full ownership of everything under /workspace so opencode and its
139
+ # agents can freely create directories and files (presentations, output, etc.)
140
+ chown -R abc:abc "$CONFIG_DIR" 2>/dev/null
141
+
142
+ touch "$MARKER"
143
+ echo "[heyagi] Customization complete."
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/with-contenv bash
2
+ # Kortix environment setup — runs once on container start (s6 cont-init.d)
3
+ #
4
+ # In cloud/production mode, overrides SDK base URLs to route through
5
+ # the Kortix router proxy. In local mode, does nothing.
6
+
7
+ if [ "$ENV_MODE" = "cloud" ] || [ "$ENV_MODE" = "production" ]; then
8
+ echo "[Kortix] Cloud mode — enabling API proxy routing"
9
+
10
+ if [ -z "$KORTIX_API_URL" ]; then
11
+ echo "[Kortix] WARNING: KORTIX_API_URL is empty — LLM calls will fail until it is set via /env API"
12
+ echo "[Kortix] Services will still start; set KORTIX_API_URL later to enable model routing"
13
+ else
14
+ # Write env overrides that s6 services will inherit via with-contenv
15
+ printf '%s' "${KORTIX_API_URL}/tavily" > /var/run/s6/container_environment/TAVILY_API_URL
16
+ printf '%s' "${KORTIX_API_URL}/serper" > /var/run/s6/container_environment/SERPER_API_URL
17
+ printf '%s' "${KORTIX_API_URL}/firecrawl" > /var/run/s6/container_environment/FIRECRAWL_API_URL
18
+ printf '%s' "${KORTIX_API_URL}/replicate" > /var/run/s6/container_environment/REPLICATE_API_URL
19
+ printf '%s' "${KORTIX_API_URL}/context7" > /var/run/s6/container_environment/CONTEXT7_API_URL
20
+
21
+ echo "[Kortix] SDK URLs routed through ${KORTIX_API_URL}"
22
+ fi
23
+ else
24
+ echo "[Kortix] Local mode — proxy routing disabled"
25
+ fi
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@kortix/sandbox-master",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "bun run --watch src/index.ts",
7
+ "start": "bun run src/index.ts",
8
+ "test": "bun test",
9
+ "test:unit": "bun test tests/unit/",
10
+ "test:integration": "bun test tests/integration/",
11
+ "test:e2e": "bun test tests/e2e/",
12
+ "test:coverage": "bun test --coverage"
13
+ },
14
+ "dependencies": {
15
+ "hono": "^4.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "bun-types": "^1.0.0",
19
+ "typescript": "^5.0.0",
20
+ "@types/node": "^20.0.0"
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ export const config = {
2
+ // Kortix Master port (main entry point)
3
+ PORT: parseInt(process.env.KORTIX_MASTER_PORT || '8000'),
4
+
5
+ // OpenCode server (proxied)
6
+ OPENCODE_HOST: process.env.OPENCODE_HOST || 'localhost',
7
+ OPENCODE_PORT: parseInt(process.env.OPENCODE_PORT || '4096'),
8
+ OPENCODE_USERNAME: process.env.OPENCODE_SERVER_USERNAME || '',
9
+ OPENCODE_PASSWORD: process.env.OPENCODE_SERVER_PASSWORD || '',
10
+
11
+ // Kortix backend
12
+ KORTIX_API_URL: process.env.KORTIX_API_URL || 'https://api.kortix.ai',
13
+ KORTIX_TOKEN: process.env.KORTIX_TOKEN || '',
14
+
15
+ // Secret storage
16
+ SECRET_FILE_PATH: process.env.SECRET_FILE_PATH || '/app/secrets/.secrets.json',
17
+ SALT_FILE_PATH: process.env.SALT_FILE_PATH || '/app/secrets/.salt',
18
+
19
+ // Sandbox metadata
20
+ SANDBOX_ID: process.env.SANDBOX_ID || '',
21
+ PROJECT_ID: process.env.PROJECT_ID || '',
22
+ }
@@ -0,0 +1,44 @@
1
+ import { Hono } from 'hono'
2
+ import { cors } from 'hono/cors'
3
+ import { logger } from 'hono/logger'
4
+ import { proxyToOpenCode } from './services/proxy'
5
+ import { SecretStore } from './services/secret-store'
6
+ import envRouter from './routes/env'
7
+ import proxyRouter from './routes/proxy'
8
+ import updateRouter from './routes/update'
9
+ import { config } from './config'
10
+
11
+ const app = new Hono()
12
+
13
+ // Initialize secret store and load ENV variables
14
+ const secretStore = new SecretStore()
15
+ await secretStore.loadIntoProcessEnv()
16
+
17
+ // Global middleware
18
+ app.use('*', logger())
19
+ app.use('*', cors())
20
+
21
+ // Health check
22
+ app.get('/kortix/health', (c) => c.json({ status: 'ok' }))
23
+
24
+ // Update check — /kortix/update and /kortix/update/status
25
+ app.route('/kortix/update', updateRouter)
26
+
27
+ // ENV management routes
28
+ app.route('/env', envRouter)
29
+
30
+ // Dynamic port proxy — /proxy/:port/* forwards to localhost:{port} inside the sandbox
31
+ app.route('/proxy', proxyRouter)
32
+
33
+ // Proxy all other requests to OpenCode
34
+ app.all('*', async (c) => {
35
+ return proxyToOpenCode(c)
36
+ })
37
+
38
+ console.log(`[Kortix Master] Starting on port ${config.PORT}`)
39
+ console.log(`[Kortix Master] Proxying to OpenCode at ${config.OPENCODE_HOST}:${config.OPENCODE_PORT}`)
40
+
41
+ export default {
42
+ port: config.PORT,
43
+ fetch: app.fetch,
44
+ }
@@ -0,0 +1,65 @@
1
+ import { Hono } from 'hono'
2
+ import { SecretStore } from '../services/secret-store'
3
+
4
+ const envRouter = new Hono()
5
+ const secretStore = new SecretStore()
6
+
7
+ // GET /env - list all ENV vars
8
+ envRouter.get('/', async (c) => {
9
+ try {
10
+ const envVars = await secretStore.getAll()
11
+ return c.json(envVars)
12
+ } catch (error) {
13
+ console.error('[ENV API] Error listing environment variables:', error)
14
+ return c.json({ error: 'Failed to list environment variables' }, 500)
15
+ }
16
+ })
17
+
18
+ // GET /env/:key - get specific ENV var
19
+ envRouter.get('/:key', async (c) => {
20
+ try {
21
+ const key = c.req.param('key')
22
+ const value = await secretStore.get(key)
23
+ if (value === null) {
24
+ return c.json({ error: 'Environment variable not found' }, 404)
25
+ }
26
+ return c.json({ [key]: value })
27
+ } catch (error) {
28
+ console.error('[ENV API] Error getting environment variable:', error)
29
+ return c.json({ error: 'Failed to get environment variable' }, 500)
30
+ }
31
+ })
32
+
33
+ // POST /env/:key - set ENV var
34
+ envRouter.post('/:key', async (c) => {
35
+ try {
36
+ const key = c.req.param('key')
37
+ const body = await c.req.json()
38
+
39
+ if (!body || typeof body.value !== 'string') {
40
+ return c.json({ error: 'Request body must contain a "value" field with string value' }, 400)
41
+ }
42
+
43
+ await secretStore.setEnv(key, body.value)
44
+ console.log(`[ENV API] Set environment variable: ${key}`)
45
+ return c.json({ message: 'Environment variable set', key, value: body.value })
46
+ } catch (error) {
47
+ console.error('[ENV API] Error setting environment variable:', error)
48
+ return c.json({ error: 'Failed to set environment variable' }, 500)
49
+ }
50
+ })
51
+
52
+ // DELETE /env/:key - delete ENV var
53
+ envRouter.delete('/:key', async (c) => {
54
+ try {
55
+ const key = c.req.param('key')
56
+ await secretStore.deleteEnv(key)
57
+ console.log(`[ENV API] Deleted environment variable: ${key}`)
58
+ return c.json({ message: 'Environment variable deleted', key })
59
+ } catch (error) {
60
+ console.error('[ENV API] Error deleting environment variable:', error)
61
+ return c.json({ error: 'Failed to delete environment variable' }, 500)
62
+ }
63
+ })
64
+
65
+ export default envRouter
@@ -0,0 +1,108 @@
1
+ import { Hono } from 'hono'
2
+ import { config } from '../config'
3
+
4
+ const proxyRouter = new Hono()
5
+
6
+ // Blocked ports — prevent proxying to kortix-master itself or other sensitive services
7
+ const BLOCKED_PORTS = new Set([
8
+ config.PORT, // kortix-master itself (default 8000)
9
+ ])
10
+
11
+ /**
12
+ * Dynamic port proxy: /proxy/:port/*
13
+ *
14
+ * Proxies HTTP requests to any localhost port inside the sandbox container.
15
+ * This enables the frontend to access any service the agent starts
16
+ * (e.g. dev servers on port 3000, 8080, 5173, etc.) without needing
17
+ * those ports individually exposed in docker-compose.
18
+ *
19
+ * In cloud mode, the frontend accesses this via:
20
+ * https://kortix.cloud/{sandboxId}/8000/proxy/{port}/{path}
21
+ *
22
+ * In local mode:
23
+ * http://localhost:8000/proxy/{port}/{path}
24
+ */
25
+ proxyRouter.all('/:port{[0-9]+}/*', async (c) => {
26
+ const portStr = c.req.param('port')
27
+ const port = parseInt(portStr, 10)
28
+
29
+ // Validate port
30
+ if (isNaN(port) || port < 1 || port > 65535) {
31
+ return c.json({ error: 'Invalid port number', port: portStr }, 400)
32
+ }
33
+
34
+ if (BLOCKED_PORTS.has(port)) {
35
+ return c.json({ error: 'Port is blocked', port }, 403)
36
+ }
37
+
38
+ // Extract the path after /proxy/:port/
39
+ const url = new URL(c.req.url)
40
+ const prefix = `/proxy/${portStr}`
41
+ const remainingPath = url.pathname.slice(prefix.length) || '/'
42
+ const targetUrl = `http://localhost:${port}${remainingPath}${url.search}`
43
+
44
+ // Build headers, stripping Host (it would be wrong for the upstream)
45
+ const headers = new Headers()
46
+ for (const [key, value] of c.req.raw.headers.entries()) {
47
+ const lower = key.toLowerCase()
48
+ if (lower === 'host' || lower === 'authorization') continue
49
+ headers.set(key, value)
50
+ }
51
+
52
+ // Set correct Host for the upstream service
53
+ headers.set('Host', `localhost:${port}`)
54
+
55
+ try {
56
+ const response = await fetch(targetUrl, {
57
+ method: c.req.method,
58
+ headers,
59
+ body: c.req.method !== 'GET' && c.req.method !== 'HEAD'
60
+ ? await c.req.raw.clone().arrayBuffer()
61
+ : undefined,
62
+ // @ts-ignore - Bun supports duplex
63
+ duplex: 'half',
64
+ redirect: 'manual',
65
+ })
66
+
67
+ // Rewrite Location headers so redirects go through the proxy too
68
+ const responseHeaders = new Headers(response.headers)
69
+ const location = responseHeaders.get('location')
70
+ if (location) {
71
+ try {
72
+ const locUrl = new URL(location, `http://localhost:${port}`)
73
+ // Only rewrite if the redirect target is the same localhost port
74
+ if (locUrl.hostname === 'localhost' && parseInt(locUrl.port || '80') === port) {
75
+ responseHeaders.set('location', `${prefix}${locUrl.pathname}${locUrl.search}`)
76
+ }
77
+ } catch {
78
+ // Leave location as-is if we can't parse it
79
+ }
80
+ }
81
+
82
+ return new Response(response.body, {
83
+ status: response.status,
84
+ statusText: response.statusText,
85
+ headers: responseHeaders,
86
+ })
87
+ } catch (error) {
88
+ console.error(`[Kortix Master] Port proxy error (port ${port}):`, error)
89
+ return c.json(
90
+ {
91
+ error: 'Failed to connect to service',
92
+ port,
93
+ details: String(error),
94
+ hint: `No service appears to be running on port ${port} inside the sandbox.`,
95
+ },
96
+ 502
97
+ )
98
+ }
99
+ })
100
+
101
+ // Handle bare /proxy/:port (no trailing path) — redirect to /proxy/:port/
102
+ proxyRouter.all('/:port{[0-9]+}', async (c) => {
103
+ const portStr = c.req.param('port')
104
+ const url = new URL(c.req.url)
105
+ return c.redirect(`/proxy/${portStr}/${url.search}`, 301)
106
+ })
107
+
108
+ export default proxyRouter
@@ -0,0 +1,185 @@
1
+ import { Hono } from 'hono';
2
+ import { config } from '../config';
3
+
4
+ // ─── Types ──────────────────────────────────────────────────────────────────
5
+
6
+ interface LocalVersion {
7
+ version: string;
8
+ updatedAt: string;
9
+ }
10
+
11
+ // ─── Constants ──────────────────────────────────────────────────────────────
12
+
13
+ const VERSION_FILE = '/opt/kortix/.version';
14
+
15
+ /**
16
+ * Services to restart after update. Order matters:
17
+ * - opencode first (depends on /opt/opencode/)
18
+ * - then other services
19
+ * - kortix-master LAST (deferred — it's us)
20
+ */
21
+ const SERVICES_TO_RESTART = [
22
+ 'opencode-serve',
23
+ 'opencode-web',
24
+ 'lss-sync',
25
+ 'agent-browser-viewer',
26
+ 'KORTIX-presentation-viewer',
27
+ ];
28
+
29
+ // ─── State ──────────────────────────────────────────────────────────────────
30
+
31
+ let updateInProgress = false;
32
+
33
+ // ─── Helpers ────────────────────────────────────────────────────────────────
34
+
35
+ async function readLocalVersion(): Promise<LocalVersion> {
36
+ try {
37
+ const file = Bun.file(VERSION_FILE);
38
+ if (await file.exists()) {
39
+ return await file.json();
40
+ }
41
+ } catch (e) {
42
+ console.error('[Update] Failed to read version file:', e);
43
+ }
44
+ return { version: '0.0.0', updatedAt: '' };
45
+ }
46
+
47
+ async function fetchLatestVersion(): Promise<string | null> {
48
+ const url = `${config.KORTIX_API_URL}/v1/sandbox/version`;
49
+ try {
50
+ const res = await fetch(url, {
51
+ headers: { 'Accept': 'application/json' },
52
+ signal: AbortSignal.timeout(10_000),
53
+ });
54
+ if (!res.ok) {
55
+ console.error(`[Update] Version fetch failed: ${res.status}`);
56
+ return null;
57
+ }
58
+ const data = await res.json() as { version: string };
59
+ return data.version;
60
+ } catch (e) {
61
+ console.error('[Update] Failed to fetch version:', e);
62
+ return null;
63
+ }
64
+ }
65
+
66
+ async function run(cmd: string): Promise<{ ok: boolean; output: string }> {
67
+ try {
68
+ const proc = Bun.spawn(['bash', '-c', cmd], {
69
+ stdout: 'pipe',
70
+ stderr: 'pipe',
71
+ env: { ...process.env, HOME: '/workspace' },
72
+ });
73
+ const [stdout, stderr] = await Promise.all([
74
+ new Response(proc.stdout).text(),
75
+ new Response(proc.stderr).text(),
76
+ ]);
77
+ const exitCode = await proc.exited;
78
+ return { ok: exitCode === 0, output: (stdout + '\n' + stderr).trim() };
79
+ } catch (e) {
80
+ return { ok: false, output: String(e) };
81
+ }
82
+ }
83
+
84
+ async function restartService(name: string): Promise<void> {
85
+ await run(`s6-svc -r /var/run/s6/services/${name} 2>/dev/null || s6-svc -r /etc/services.d/${name} 2>/dev/null || true`);
86
+ }
87
+
88
+ async function performUpdate(targetVersion: string): Promise<{
89
+ success: boolean;
90
+ output: string;
91
+ }> {
92
+ console.log(`[Update] Installing @kortix/sandbox@${targetVersion}...`);
93
+
94
+ const result = await run(`npm install -g @kortix/sandbox@${targetVersion} 2>&1`);
95
+
96
+ if (!result.ok) {
97
+ console.error('[Update] npm install failed:', result.output);
98
+ return { success: false, output: result.output.slice(0, 1000) };
99
+ }
100
+
101
+ console.log('[Update] Install complete, restarting services...');
102
+
103
+ for (const svc of SERVICES_TO_RESTART) {
104
+ console.log(`[Update] Restarting: ${svc}`);
105
+ await restartService(svc);
106
+ }
107
+
108
+ // Self-restart deferred so the HTTP response completes
109
+ console.log('[Update] Scheduling kortix-master restart in 2s...');
110
+ setTimeout(() => restartService('kortix-master'), 2000);
111
+
112
+ return { success: true, output: result.output.slice(0, 1000) };
113
+ }
114
+
115
+ // ─── Routes ─────────────────────────────────────────────────────────────────
116
+
117
+ const updateRouter = new Hono();
118
+
119
+ /**
120
+ * GET /kortix/update/status
121
+ *
122
+ * Returns current sandbox version + latest available version.
123
+ * Frontend uses this to decide whether to show "Update available".
124
+ * Read-only — does NOT trigger any update.
125
+ */
126
+ updateRouter.get('/status', async (c) => {
127
+ const local = await readLocalVersion();
128
+ const latest = await fetchLatestVersion();
129
+
130
+ return c.json({
131
+ currentVersion: local.version,
132
+ latestVersion: latest || 'unknown',
133
+ updateAvailable: latest ? local.version !== latest : false,
134
+ updatedAt: local.updatedAt,
135
+ updateInProgress,
136
+ });
137
+ });
138
+
139
+ /**
140
+ * POST /kortix/update
141
+ *
142
+ * User-triggered update. Fetches latest version, installs the package,
143
+ * restarts services. Only runs when explicitly called.
144
+ */
145
+ updateRouter.post('/', async (c) => {
146
+ if (updateInProgress) {
147
+ return c.json({ error: 'Update already in progress' }, 409);
148
+ }
149
+
150
+ updateInProgress = true;
151
+ try {
152
+ const local = await readLocalVersion();
153
+ const latestVersion = await fetchLatestVersion();
154
+
155
+ if (!latestVersion) {
156
+ return c.json({ error: 'Could not reach version service' }, 502);
157
+ }
158
+
159
+ if (local.version === latestVersion) {
160
+ return c.json({
161
+ upToDate: true,
162
+ currentVersion: local.version,
163
+ latestVersion,
164
+ });
165
+ }
166
+
167
+ console.log(`[Update] User triggered: ${local.version} -> ${latestVersion}`);
168
+ const update = await performUpdate(latestVersion);
169
+
170
+ return c.json({
171
+ success: update.success,
172
+ previousVersion: local.version,
173
+ currentVersion: update.success ? latestVersion : local.version,
174
+ latestVersion,
175
+ output: update.output,
176
+ });
177
+ } catch (e) {
178
+ console.error('[Update] Error:', e);
179
+ return c.json({ error: 'Update failed', details: String(e) }, 500);
180
+ } finally {
181
+ updateInProgress = false;
182
+ }
183
+ });
184
+
185
+ export default updateRouter;
@@ -0,0 +1,43 @@
1
+ import type { Context } from 'hono'
2
+ import { config } from '../config'
3
+
4
+ export async function proxyToOpenCode(c: Context): Promise<Response> {
5
+ const url = new URL(c.req.url)
6
+ const targetUrl = `http://${config.OPENCODE_HOST}:${config.OPENCODE_PORT}${url.pathname}${url.search}`
7
+
8
+ // Build headers, forwarding most but not Host
9
+ const headers = new Headers()
10
+ for (const [key, value] of c.req.raw.headers.entries()) {
11
+ if (key.toLowerCase() !== 'host') {
12
+ headers.set(key, value)
13
+ }
14
+ }
15
+
16
+ // Add OpenCode basic auth if configured
17
+ if (config.OPENCODE_USERNAME && config.OPENCODE_PASSWORD) {
18
+ const auth = Buffer.from(`${config.OPENCODE_USERNAME}:${config.OPENCODE_PASSWORD}`).toString('base64')
19
+ headers.set('Authorization', `Basic ${auth}`)
20
+ }
21
+
22
+ try {
23
+ const response = await fetch(targetUrl, {
24
+ method: c.req.method,
25
+ headers,
26
+ body: c.req.method !== 'GET' && c.req.method !== 'HEAD'
27
+ ? await c.req.raw.clone().arrayBuffer()
28
+ : undefined,
29
+ // @ts-ignore - Bun supports duplex
30
+ duplex: 'half',
31
+ })
32
+
33
+ // Return proxied response with all headers
34
+ return new Response(response.body, {
35
+ status: response.status,
36
+ statusText: response.statusText,
37
+ headers: response.headers,
38
+ })
39
+ } catch (error) {
40
+ console.error('[Kortix Master] Proxy error:', error)
41
+ return c.json({ error: 'Failed to proxy to OpenCode', details: String(error) }, 502)
42
+ }
43
+ }