upfynai-code 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/client/dist/assets/AppContent-DTZ2FbvM.js +513 -0
  2. package/client/dist/assets/CanvasPanel-DlTW6Jh6.js +6 -0
  3. package/client/dist/assets/LoginModal-CWoFm0au.js +19 -0
  4. package/client/dist/assets/MarkdownPreview-CYdvwJaV.js +1 -0
  5. package/client/dist/assets/{Onboarding-Coxo6mFA.js → Onboarding-CtIoXiTp.js} +1 -1
  6. package/client/dist/assets/{SetupForm-BzYOsbji.js → SetupForm-B4p8im5O.js} +1 -1
  7. package/client/dist/assets/{ar-SA-G6X2FPQ2-Bmw2-hDt.js → ar-SA-G6X2FPQ2-2gfmdvHk.js} +1 -1
  8. package/client/dist/assets/{arc-BMqY7_Ci.js → arc-DCZSHhoJ.js} +1 -1
  9. package/client/dist/assets/{az-AZ-76LH7QW2-Dh1le_qs.js → az-AZ-76LH7QW2-CDdeucRZ.js} +1 -1
  10. package/client/dist/assets/{bg-BG-XCXSNQG7-Cbav8Z9z.js → bg-BG-XCXSNQG7-D6__XtOK.js} +1 -1
  11. package/client/dist/assets/{blockDiagram-38ab4fdb-ChHJxsXw.js → blockDiagram-38ab4fdb-Cfbaeyp6.js} +3 -3
  12. package/client/dist/assets/{bn-BD-2XOGV67Q-DCNjOaWz.js → bn-BD-2XOGV67Q-DHNJw3OG.js} +1 -1
  13. package/client/dist/assets/{c4Diagram-3d4e48cf-b8Xue4Z6.js → c4Diagram-3d4e48cf-BBCnjOTy.js} +1 -1
  14. package/client/dist/assets/{ca-ES-6MX7JW3Y-Dl_vM7NS.js → ca-ES-6MX7JW3Y-r5g4o3zQ.js} +1 -1
  15. package/client/dist/assets/channel-O3ovC0x9.js +1 -0
  16. package/client/dist/assets/{classDiagram-70f12bd4-BheP7Ggo.js → classDiagram-70f12bd4-D0lhAcxU.js} +1 -1
  17. package/client/dist/assets/classDiagram-v2-f2320105-BuwUsF3F.js +2 -0
  18. package/client/dist/assets/clone-BG9u7vLi.js +1 -0
  19. package/client/dist/assets/{createText-2e5e7dd3-_n4jI_fO.js → createText-2e5e7dd3-B8jCDmF_.js} +1 -1
  20. package/client/dist/assets/{cs-CZ-2BRQDIVT-ftsKDdz4.js → cs-CZ-2BRQDIVT-p08jRLRC.js} +1 -1
  21. package/client/dist/assets/{da-DK-5WZEPLOC-DAjdwGRO.js → da-DK-5WZEPLOC-CnhOImFf.js} +1 -1
  22. package/client/dist/assets/{de-DE-XR44H4JA-BJXczHGT.js → de-DE-XR44H4JA-BunSXZ-Y.js} +1 -1
  23. package/client/dist/assets/{edges-e0da2a9e-CfPZr4YM.js → edges-e0da2a9e-CGBBhG8k.js} +2 -2
  24. package/client/dist/assets/{el-GR-BZB4AONW-DW2p_uy7.js → el-GR-BZB4AONW-D4wv1oIz.js} +1 -1
  25. package/client/dist/assets/{erDiagram-9861fffd-CF33V-Of.js → erDiagram-9861fffd-CYaF3q1I.js} +1 -1
  26. package/client/dist/assets/{es-ES-U4NZUMDT-DLOIGnrl.js → es-ES-U4NZUMDT-CGeTKXgd.js} +1 -1
  27. package/client/dist/assets/{eu-ES-A7QVB2H4-LJXbf89m.js → eu-ES-A7QVB2H4-Cayx1TxR.js} +1 -1
  28. package/client/dist/assets/{fa-IR-HGAKTJCU-Dvx65fgW.js → fa-IR-HGAKTJCU-CmUg8pmw.js} +1 -1
  29. package/client/dist/assets/{fi-FI-Z5N7JZ37-EoL65BQh.js → fi-FI-Z5N7JZ37-xvHcPhsU.js} +1 -1
  30. package/client/dist/assets/{flowDb-956e92f1-HgoXVy2H.js → flowDb-956e92f1-C-_LFz70.js} +3 -3
  31. package/client/dist/assets/flowDiagram-66a62f08-C1sHdSjn.js +4 -0
  32. package/client/dist/assets/flowDiagram-v2-96b9c2cf-Cd0Iascd.js +1 -0
  33. package/client/dist/assets/{flowchart-elk-definition-4a651766-DJbI2dpv.js → flowchart-elk-definition-4a651766-CNGfpudb.js} +7 -7
  34. package/client/dist/assets/{fr-FR-RHASNOE6-DNk_jdDs.js → fr-FR-RHASNOE6-DBoHEcNj.js} +1 -1
  35. package/client/dist/assets/{ganttDiagram-c361ad54-2XX670FU.js → ganttDiagram-c361ad54-B8HJQqjt.js} +1 -1
  36. package/client/dist/assets/{gitGraphDiagram-72cf32ee-CcUfruAo.js → gitGraphDiagram-72cf32ee-DojCDvlS.js} +1 -1
  37. package/client/dist/assets/{gl-ES-HMX3MZ6V-dxzFjZlG.js → gl-ES-HMX3MZ6V-p6hrn2cN.js} +1 -1
  38. package/client/dist/assets/{graph-BSbiMSBC.js → graph-DXM7lcy1.js} +1 -1
  39. package/client/dist/assets/{he-IL-6SHJWFNN-Cogsfdt1.js → he-IL-6SHJWFNN-y2jEX6-0.js} +1 -1
  40. package/client/dist/assets/{hi-IN-IWLTKZ5I-L6wbgi4F.js → hi-IN-IWLTKZ5I-99pNfyWr.js} +1 -1
  41. package/client/dist/assets/{hu-HU-A5ZG7DT2-DSA6ZDsH.js → hu-HU-A5ZG7DT2-hygceGMS.js} +1 -1
  42. package/client/dist/assets/{id-ID-SAP4L64H-BK_vGGS6.js → id-ID-SAP4L64H-CyIqi1hv.js} +1 -1
  43. package/client/dist/assets/{image-blob-reduce.esm-BLtmMM_J.js → image-blob-reduce.esm-D6s-rqMO.js} +6 -1
  44. package/client/dist/assets/{index-3862675e-Bv32HUgT.js → index-3862675e-4idOQN2N.js} +1 -1
  45. package/client/dist/assets/{index-BPwf8Fw3.js → index-BGmwbRlb.js} +6 -6
  46. package/client/dist/assets/index-BHZfFT_V.js +97 -0
  47. package/client/dist/assets/{infoDiagram-f8f76790-w4mR4pxn.js → infoDiagram-f8f76790-CFLrHqtc.js} +1 -1
  48. package/client/dist/assets/{it-IT-JPQ66NNP-BLdHYMhn.js → it-IT-JPQ66NNP-DzVvVdQI.js} +1 -1
  49. package/client/dist/assets/{ja-JP-DBVTYXUO-B_vmexl_.js → ja-JP-DBVTYXUO-BI4fPexV.js} +1 -1
  50. package/client/dist/assets/{journeyDiagram-49397b02-D9nmO17e.js → journeyDiagram-49397b02-C3CFDo8z.js} +1 -1
  51. package/client/dist/assets/{kaa-6HZHGXH3-5s-3jl6F.js → kaa-6HZHGXH3-fwOleoQB.js} +1 -1
  52. package/client/dist/assets/{kab-KAB-ZGHBKWFO-2QaVDuSf.js → kab-KAB-ZGHBKWFO-DBI_ri48.js} +1 -1
  53. package/client/dist/assets/{kk-KZ-P5N5QNE5-CTC52Vbi.js → kk-KZ-P5N5QNE5-zpl7uvyF.js} +1 -1
  54. package/client/dist/assets/{km-KH-HSX4SM5Z-DxawH8UZ.js → km-KH-HSX4SM5Z-DOMFSres.js} +1 -1
  55. package/client/dist/assets/{ko-KR-MTYHY66A-CmosEM8_.js → ko-KR-MTYHY66A-tb08hXzd.js} +1 -1
  56. package/client/dist/assets/{ku-TR-6OUDTVRD-DbiLen4y.js → ku-TR-6OUDTVRD-DlIQCCY4.js} +1 -1
  57. package/client/dist/assets/{layout-jmt3H9tA.js → layout-B_11mCXA.js} +1 -1
  58. package/client/dist/assets/{line-JTlRayUJ.js → line-B-qmK_vI.js} +1 -1
  59. package/client/dist/assets/{linear-DJeB5p7x.js → linear-Ph6uuYcX.js} +1 -1
  60. package/client/dist/assets/{lt-LT-XHIRWOB4-CH15wrjA.js → lt-LT-XHIRWOB4--qWy24_Z.js} +1 -1
  61. package/client/dist/assets/{lv-LV-5QDEKY6T-dhgfPuCQ.js → lv-LV-5QDEKY6T-Bnd_1GDb.js} +1 -1
  62. package/client/dist/assets/mindmap-definition-fc14e90a-Do79tIc0.js +425 -0
  63. package/client/dist/assets/{mr-IN-CRQNXWMA-3Gi6iq7A.js → mr-IN-CRQNXWMA-BsV6HaD9.js} +1 -1
  64. package/client/dist/assets/{my-MM-5M5IBNSE-CpH4rdJj.js → my-MM-5M5IBNSE-kZQURVIi.js} +1 -1
  65. package/client/dist/assets/{nb-NO-T6EIAALU-Du6iiGql.js → nb-NO-T6EIAALU-Cvf9FdSF.js} +1 -1
  66. package/client/dist/assets/{nl-NL-IS3SIHDZ-BGvsd1MT.js → nl-NL-IS3SIHDZ-DA1yqpXw.js} +1 -1
  67. package/client/dist/assets/{nn-NO-6E72VCQL-B-odvJZW.js → nn-NO-6E72VCQL-89lm3vku.js} +1 -1
  68. package/client/dist/assets/{oc-FR-POXYY2M6-COC8xNjo.js → oc-FR-POXYY2M6-BsrjTJQh.js} +1 -1
  69. package/client/dist/assets/{pa-IN-N4M65BXN-CE21PUQH.js → pa-IN-N4M65BXN-CczefYaj.js} +1 -1
  70. package/client/dist/assets/pdf-CE_K4jFx.js +12 -0
  71. package/client/dist/assets/percentages-BXMCSKIN-Be6p9phi.js +207 -0
  72. package/client/dist/assets/pica-CQIY57Tf.js +7 -0
  73. package/client/dist/assets/{pieDiagram-8a3498a8-Cvfh7Qr5.js → pieDiagram-8a3498a8-CfblQHdm.js} +2 -2
  74. package/client/dist/assets/{pl-PL-T2D74RX3-D4xFVSoT.js → pl-PL-T2D74RX3-DdhH-zcK.js} +1 -1
  75. package/client/dist/assets/{pt-BR-5N22H2LF-CCq257gA.js → pt-BR-5N22H2LF-gpwlheL6.js} +1 -1
  76. package/client/dist/assets/{pt-PT-UZXXM6DQ-1l8gt5vA.js → pt-PT-UZXXM6DQ-Cs87vICi.js} +1 -1
  77. package/client/dist/assets/{quadrantDiagram-120e2f19-BA0js1aD.js → quadrantDiagram-120e2f19-CRMSamSP.js} +1 -1
  78. package/client/dist/assets/{requirementDiagram-deff3bca-B0QNFfIn.js → requirementDiagram-deff3bca-D3LBN016.js} +1 -1
  79. package/client/dist/assets/{ro-RO-JPDTUUEW-yosBW01E.js → ro-RO-JPDTUUEW-CWTSJ1Dt.js} +1 -1
  80. package/client/dist/assets/roundRect-0PYZxl1G.js +1 -0
  81. package/client/dist/assets/{ru-RU-B4JR7IUQ-8LkEJUix.js → ru-RU-B4JR7IUQ-Bq7aN2ep.js} +1 -1
  82. package/client/dist/assets/{sankeyDiagram-04a897e0-D4T9eCXn.js → sankeyDiagram-04a897e0-CsFqOQZN.js} +3 -3
  83. package/client/dist/assets/{sequenceDiagram-704730f1-CfBUTCrO.js → sequenceDiagram-704730f1-BRYXVDGX.js} +1 -1
  84. package/client/dist/assets/{si-LK-N5RQ5JYF-D8rjbqtd.js → si-LK-N5RQ5JYF-BBjcNYQh.js} +1 -1
  85. package/client/dist/assets/{sk-SK-C5VTKIMK-Bg14sAzN.js → sk-SK-C5VTKIMK-ByjKQzUb.js} +1 -1
  86. package/client/dist/assets/{sl-SI-NN7IZMDC-CMTib6Zs.js → sl-SI-NN7IZMDC-B8WCyMBU.js} +1 -1
  87. package/client/dist/assets/{stateDiagram-587899a1-BGgvmVSZ.js → stateDiagram-587899a1-BHoy9LtD.js} +1 -1
  88. package/client/dist/assets/{stateDiagram-v2-d93cdb3a-Qn3DpYuO.js → stateDiagram-v2-d93cdb3a-BvMUA6bS.js} +1 -1
  89. package/client/dist/assets/{styles-6aaf32cf-IdVZLPrD.js → styles-6aaf32cf-Dr-lfIOW.js} +2 -2
  90. package/client/dist/assets/{styles-9a916d00-BAC3L45X.js → styles-9a916d00-DS4wRpL7.js} +1 -1
  91. package/client/dist/assets/{styles-c10674c1-COhXxX8c.js → styles-c10674c1-nKRF6NrH.js} +1 -1
  92. package/client/dist/assets/{subset-shared.chunk-BWHnFai4.js → subset-shared.chunk-KT79s7KG.js} +64 -2
  93. package/client/dist/assets/subset-worker.chunk-BMx1eyv3.js +1 -0
  94. package/client/dist/assets/{sv-SE-XGPEYMSR-C1425rOF.js → sv-SE-XGPEYMSR-BiIPUVbv.js} +1 -1
  95. package/client/dist/assets/{svgDrawCommon-08f97a94-Cfk-fgnN.js → svgDrawCommon-08f97a94-C3uP9PYr.js} +1 -1
  96. package/client/dist/assets/{ta-IN-2NMHFXQM-BHHo1zpF.js → ta-IN-2NMHFXQM-Cidadso2.js} +1 -1
  97. package/client/dist/assets/{th-TH-HPSO5L25-CZVzm_WT.js → th-TH-HPSO5L25-CFNnJwSv.js} +1 -1
  98. package/client/dist/assets/{timeline-definition-85554ec2-VAvuJith.js → timeline-definition-85554ec2-BSsLsIgF.js} +1 -1
  99. package/client/dist/assets/{tr-TR-DEFEU3FU-DE1lclCq.js → tr-TR-DEFEU3FU-DaFcI-KL.js} +1 -1
  100. package/client/dist/assets/{uk-UA-QMV73CPH-D4lJZ85O.js → uk-UA-QMV73CPH-DkBW36St.js} +1 -1
  101. package/client/dist/assets/vendor-codemirror-langs-BH1ZcKHY.js +20 -0
  102. package/client/dist/assets/vendor-codemirror-rix45NST.js +16 -0
  103. package/client/dist/assets/vendor-i18n-DCFGyhQR.js +1 -0
  104. package/client/dist/assets/vendor-icons-Dh9m_Ydt.js +596 -0
  105. package/client/dist/assets/{vendor-markdown-CIVH08vJ.js → vendor-markdown-BXEi_H3G.js} +3 -3
  106. package/client/dist/assets/vendor-react-9mUTKBHH.js +67 -0
  107. package/client/dist/assets/{vendor-syntax-Djb62v3a.js → vendor-syntax-DnmwQQJF.js} +14 -7
  108. package/client/dist/assets/vendor-xterm-CZq1hqo1.js +66 -0
  109. package/client/dist/assets/vendor-xterm-qxJ8_QYu.css +32 -0
  110. package/client/dist/assets/{vi-VN-M7AON7JQ-Dgc_SShk.js → vi-VN-M7AON7JQ-KrtfxOzl.js} +1 -1
  111. package/client/dist/assets/{xychartDiagram-e933f94c-BeyVBJhb.js → xychartDiagram-e933f94c-CgNgZ4pp.js} +1 -1
  112. package/client/dist/assets/{zh-CN-LNUGB5OW-MH4Yh8in.js → zh-CN-LNUGB5OW-BQu12RoD.js} +1 -1
  113. package/client/dist/assets/{zh-HK-E62DVLB3-D4XHehjx.js → zh-HK-E62DVLB3-zx9CvERq.js} +1 -1
  114. package/client/dist/assets/{zh-TW-RAJ6MFWO--efj3evj.js → zh-TW-RAJ6MFWO-ffJWgVxn.js} +1 -1
  115. package/client/dist/index.html +6 -6
  116. package/commands/upfynai-connect.md +31 -18
  117. package/commands/upfynai.md +45 -26
  118. package/package.json +1 -1
  119. package/server/cli-ui.js +785 -0
  120. package/server/cli.js +235 -161
  121. package/server/index.js +159 -107
  122. package/server/middleware/auth.js +9 -5
  123. package/server/openrouter.js +137 -0
  124. package/server/relay-client.js +158 -47
  125. package/server/routes/agent.js +54 -19
  126. package/server/routes/auth.js +59 -22
  127. package/server/routes/settings.js +91 -0
  128. package/shared/modelConstants.js +29 -0
  129. package/client/dist/assets/AppContent-CTSHQdyq.js +0 -513
  130. package/client/dist/assets/CanvasPanel-Cig0Mo9s.js +0 -6
  131. package/client/dist/assets/LoginModal-silya-zP.js +0 -11
  132. package/client/dist/assets/MarkdownPreview-B3c7OEj6.js +0 -1
  133. package/client/dist/assets/channel-CSnvHe_M.js +0 -1
  134. package/client/dist/assets/classDiagram-v2-f2320105-xtym7GEZ.js +0 -2
  135. package/client/dist/assets/clone-B75abXxS.js +0 -1
  136. package/client/dist/assets/flowDiagram-66a62f08-tffoET0H.js +0 -4
  137. package/client/dist/assets/flowDiagram-v2-96b9c2cf-Byc3JCHh.js +0 -1
  138. package/client/dist/assets/index-D1urGMYu.js +0 -95
  139. package/client/dist/assets/mindmap-definition-fc14e90a-BOOrexmz.js +0 -415
  140. package/client/dist/assets/pdf-TYrZqVzP.js +0 -12
  141. package/client/dist/assets/percentages-BXMCSKIN-C9GT0OD3.js +0 -199
  142. package/client/dist/assets/pica-VkdyTzi8.js +0 -2
  143. package/client/dist/assets/roundRect-mAH3dD0p.js +0 -1
  144. package/client/dist/assets/subset-worker.chunk-C8QUSruZ.js +0 -1
  145. package/client/dist/assets/vendor-codemirror-BARtJV1V.js +0 -16
  146. package/client/dist/assets/vendor-codemirror-langs-52_y1wip.js +0 -20
  147. package/client/dist/assets/vendor-i18n-ByAl-gdx.js +0 -1
  148. package/client/dist/assets/vendor-icons-D33IkSIf.js +0 -1
  149. package/client/dist/assets/vendor-react-CHoMc7ka.js +0 -8
  150. package/client/dist/assets/vendor-xterm-DBb3RXlu.js +0 -66
  151. package/client/dist/assets/vendor-xterm-DrlLKa8f.css +0 -1
@@ -0,0 +1,785 @@
1
+ /**
2
+ * Upfyn-Code CLI — Terminal UI & Animations
3
+ *
4
+ * Beautiful blue-themed terminal experience inspired by Claude Code.
5
+ * Uses terminal-safe characters that render correctly on Windows CMD,
6
+ * PowerShell, Windows Terminal, Git Bash, macOS Terminal, and Linux.
7
+ */
8
+
9
+ // --- ANSI escape codes ---
10
+ const ESC = '\x1b';
11
+ const CSI = `${ESC}[`;
12
+
13
+ // Blue theme palette (24-bit RGB — works in all modern terminals)
14
+ const theme = {
15
+ // Core blues
16
+ blue: `${CSI}38;2;59;130;246m`, // #3B82F6
17
+ brightBlue: `${CSI}38;2;96;165;250m`, // #60A5FA
18
+ skyBlue: `${CSI}38;2;125;211;252m`, // #7DD3FC
19
+ deepBlue: `${CSI}38;2;30;58;138m`, // #1E3A8A
20
+ indigo: `${CSI}38;2;99;102;241m`, // #6366F1
21
+ violet: `${CSI}38;2;139;92;246m`, // #8B5CF6
22
+
23
+ // Accent
24
+ cyan: `${CSI}38;2;34;211;238m`, // #22D3EE
25
+ teal: `${CSI}38;2;45;212;191m`, // #2DD4BF
26
+ white: `${CSI}38;2;248;250;252m`, // #F8FAFC
27
+ gray: `${CSI}38;2;148;163;184m`, // #94A3B8
28
+ dimGray: `${CSI}38;2;71;85;105m`, // #475569
29
+ darkGray: `${CSI}38;2;51;65;85m`, // #334155
30
+ yellow: `${CSI}38;2;250;204;21m`, // #FACC15
31
+ green: `${CSI}38;2;74;222;128m`, // #4ADE80
32
+ red: `${CSI}38;2;248;113;113m`, // #F87171
33
+ orange: `${CSI}38;2;251;146;60m`, // #FB923C
34
+
35
+ // Background
36
+ bgDeep: `${CSI}48;2;15;23;42m`, // #0F172A
37
+ bgBlue: `${CSI}48;2;30;58;138m`, // #1E3A8A
38
+ bgBright: `${CSI}48;2;59;130;246m`, // #3B82F6
39
+
40
+ // Formatting
41
+ bold: `${CSI}1m`,
42
+ dim: `${CSI}2m`,
43
+ italic: `${CSI}3m`,
44
+ underline: `${CSI}4m`,
45
+ reset: `${CSI}0m`,
46
+ };
47
+
48
+ const t = theme;
49
+ const r = t.reset;
50
+
51
+ // --- Terminal capability detection ---
52
+ function detectTerminalSupport() {
53
+ // Windows Terminal, VS Code, modern terminals support full Unicode
54
+ if (process.env.WT_SESSION) return 'unicode';
55
+ if (process.env.TERM_PROGRAM === 'vscode') return 'unicode';
56
+ if (process.env.TERM_PROGRAM === 'iTerm.app') return 'unicode';
57
+ if (process.env.TERM_PROGRAM === 'Hyper') return 'unicode';
58
+ if (process.env.TERM === 'xterm-256color') return 'unicode';
59
+ if (process.env.TERM === 'xterm-kitty') return 'unicode';
60
+ if (process.env.COLORTERM === 'truecolor') return 'unicode';
61
+
62
+ // Windows without WT_SESSION — use ASCII
63
+ if (process.platform === 'win32') return 'ascii';
64
+
65
+ // Most modern Linux/Mac terminals support basic box-drawing
66
+ return 'unicode';
67
+ }
68
+
69
+ // Box-drawing character sets
70
+ const CHARS_UNICODE = {
71
+ h: '\u2500', // ─
72
+ v: '\u2502', // │
73
+ tl: '\u250C', // ┌
74
+ tr: '\u2510', // ┐
75
+ bl: '\u2514', // └
76
+ br: '\u2518', // ┘
77
+ cross: '\u253C', // ┼
78
+ ltee: '\u251C', // ├
79
+ rtee: '\u2524', // ┤
80
+ ttee: '\u252C', // ┬
81
+ btee: '\u2534', // ┴
82
+ dot: '\u00B7', // ·
83
+ bullet: '\u2022', // •
84
+ star: '*',
85
+ };
86
+
87
+ const CHARS_ASCII = {
88
+ h: '-',
89
+ v: '|',
90
+ tl: '+',
91
+ tr: '+',
92
+ bl: '+',
93
+ br: '+',
94
+ cross: '+',
95
+ ltee: '+',
96
+ rtee: '+',
97
+ ttee: '+',
98
+ btee: '+',
99
+ dot: '.',
100
+ bullet: '*',
101
+ star: '*',
102
+ };
103
+
104
+ const termLevel = detectTerminalSupport();
105
+ const ch = termLevel === 'unicode' ? CHARS_UNICODE : CHARS_ASCII;
106
+
107
+ // Helper — colorize
108
+ const c = {
109
+ blue: (s) => `${t.blue}${s}${r}`,
110
+ bright: (s) => `${t.brightBlue}${s}${r}`,
111
+ sky: (s) => `${t.skyBlue}${s}${r}`,
112
+ cyan: (s) => `${t.cyan}${s}${r}`,
113
+ teal: (s) => `${t.teal}${s}${r}`,
114
+ white: (s) => `${t.bold}${t.white}${s}${r}`,
115
+ gray: (s) => `${t.gray}${s}${r}`,
116
+ dim: (s) => `${t.dimGray}${s}${r}`,
117
+ dark: (s) => `${t.darkGray}${s}${r}`,
118
+ green: (s) => `${t.green}${s}${r}`,
119
+ yellow: (s) => `${t.yellow}${s}${r}`,
120
+ red: (s) => `${t.red}${s}${r}`,
121
+ orange: (s) => `${t.orange}${s}${r}`,
122
+ violet: (s) => `${t.violet}${s}${r}`,
123
+ indigo: (s) => `${t.indigo}${s}${r}`,
124
+ bold: (s) => `${t.bold}${s}${r}`,
125
+ bBlue: (s) => `${t.bold}${t.blue}${s}${r}`,
126
+ bBright: (s) => `${t.bold}${t.brightBlue}${s}${r}`,
127
+ bWhite: (s) => `${t.bold}${t.white}${s}${r}`,
128
+ bCyan: (s) => `${t.bold}${t.cyan}${s}${r}`,
129
+ };
130
+
131
+ // --- ASCII Art Logo ---
132
+ const LOGO = [
133
+ `${t.brightBlue} __ __ ____ ${t.skyBlue} ______ __ ${r}`,
134
+ `${t.brightBlue} / / / /___ / __/_ ______ ${t.skyBlue}/ ____/___ ____/ /__ ${r}`,
135
+ `${t.brightBlue} / / / / __ \\/ /_/ / / / __ \\ ${t.blue}____${t.skyBlue}/ / / __ \\/ __ / _ \\ ${r}`,
136
+ `${t.brightBlue} / /_/ / /_/ / __/ /_/ / / / / ${t.blue}/___ ${t.skyBlue}/ /___/ /_/ / /_/ / __/ ${r}`,
137
+ `${t.brightBlue} \\____/ .___/_/ \\__, /_/ /_/ ${t.skyBlue}\\____/\\____/\\__,_/\\___/ ${r}`,
138
+ `${t.brightBlue} /_/ /____/ ${r}`,
139
+ ];
140
+
141
+ // --- Rocket ASCII Art (safe characters only) ---
142
+ const ROCKET_FRAMES = [
143
+ // Frame 0: Pre-launch
144
+ [
145
+ ` ${t.dimGray}|${r}`,
146
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
147
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
148
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
149
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
150
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
151
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
152
+ ` ${t.dimGray}==========${r}`,
153
+ ],
154
+ // Frame 1: Ignition
155
+ [
156
+ ` ${t.dimGray}|${r}`,
157
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
158
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
159
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
160
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
161
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
162
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
163
+ ` ${t.orange}#${t.yellow}*${t.orange}#${t.yellow}*${t.orange}#${r}`,
164
+ ],
165
+ // Frame 2: Lift-off
166
+ [
167
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
168
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
169
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
170
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
171
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
172
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
173
+ ` ${t.yellow}#${t.orange}@${t.yellow}#${t.orange}@${t.yellow}#${r}`,
174
+ ` ${t.orange}*${t.yellow}o${t.orange}*${r}`,
175
+ ],
176
+ // Frame 3: Ascending
177
+ [
178
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
179
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
180
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
181
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
182
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
183
+ ` ${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${t.yellow}@${r}`,
184
+ ` ${t.orange}#${t.yellow}@${t.orange}#${r}`,
185
+ ` ${t.yellow}*${t.orange}*${r}`,
186
+ ],
187
+ // Frame 4: Full thrust
188
+ [
189
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
190
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
191
+ ` ${t.white}|${t.brightBlue}||${t.white}|${r}`,
192
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
193
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
194
+ ` ${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${r}`,
195
+ ` ${t.orange}#${t.yellow}@${t.orange}#${t.yellow}@${r}`,
196
+ ` ${t.yellow}o${t.orange}*${t.yellow}o${r}`,
197
+ ` ${t.orange}*${t.yellow}*${r}`,
198
+ ],
199
+ // Frame 5: Going up
200
+ [
201
+ ``,
202
+ ` ${t.white}/${t.brightBlue}^${t.white}\\${r}`,
203
+ ` ${t.white}|${t.blue}||${t.white}|${r}`,
204
+ ` ${t.white}/${t.blue}|||${t.white}\\${r}`,
205
+ ` ${t.white}/${t.blue}=====${t.white}\\${r}`,
206
+ ` ${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${t.yellow}@${t.orange}#${r}`,
207
+ ` ${t.orange}@${t.yellow}#${t.orange}@${t.yellow}#${r}`,
208
+ ` ${t.yellow}o${t.orange}o${t.yellow}o${r}`,
209
+ ` ${t.orange}*${r} ${t.dimGray}.${r}`,
210
+ ],
211
+ // Frame 6: Higher
212
+ [
213
+ ``,
214
+ ``,
215
+ ` ${t.skyBlue}/${t.brightBlue}^${t.skyBlue}\\${r}`,
216
+ ` ${t.skyBlue}|${t.blue}||${t.skyBlue}|${r}`,
217
+ ` ${t.skyBlue}/${t.blue}==${t.skyBlue}\\${r}`,
218
+ ` ${t.yellow}#${t.orange}@${t.yellow}#${t.orange}#${r}`,
219
+ ` ${t.orange}o${t.yellow}o${r}`,
220
+ ` ${t.yellow}*${r}`,
221
+ ` ${t.dimGray}.${r} ${t.dimGray}.${r}`,
222
+ ],
223
+ // Frame 7: Almost gone
224
+ [
225
+ ``,
226
+ ``,
227
+ ``,
228
+ ` ${t.skyBlue}/${t.brightBlue}^${t.skyBlue}\\${r}`,
229
+ ` ${t.blue}==${r}`,
230
+ ` ${t.orange}o${t.yellow}o${r}`,
231
+ ` ${t.yellow}*${r}`,
232
+ ``,
233
+ ` ${t.dimGray}.${r} ${t.dimGray}.${r} ${t.dimGray}.${r}`,
234
+ ],
235
+ // Frame 8: Gone — just stars
236
+ [
237
+ ``,
238
+ ``,
239
+ ``,
240
+ ` ${t.brightBlue}*${r}`,
241
+ ``,
242
+ ` ${t.dimGray}.${r}`,
243
+ ``,
244
+ ` ${t.dimGray}.${r} ${t.dimGray}.${r}`,
245
+ ` ${t.dimGray}.${r} ${t.dimGray}.${r} ${t.dimGray}.${r}`,
246
+ ],
247
+ ];
248
+
249
+ // --- Sleep utility ---
250
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
251
+
252
+ // --- Clear screen ---
253
+ const clearScreen = () => process.stdout.write(`${CSI}2J${CSI}H`);
254
+
255
+ // --- Move cursor ---
256
+ const moveTo = (row, col) => process.stdout.write(`${CSI}${row};${col}H`);
257
+
258
+ // --- Hide/show cursor ---
259
+ const hideCursor = () => process.stdout.write(`${CSI}?25l`);
260
+ const showCursor = () => process.stdout.write(`${CSI}?25h`);
261
+
262
+ /**
263
+ * Strip ANSI codes for length calculation
264
+ */
265
+ function stripAnsi(str) {
266
+ return str.replace(/\x1b\[[0-9;]*m/g, '');
267
+ }
268
+
269
+ /**
270
+ * Pad a string (with ANSI codes) to a visual width
271
+ */
272
+ function padAnsi(str, width) {
273
+ const visible = stripAnsi(str).length;
274
+ const pad = Math.max(0, width - visible);
275
+ return str + ' '.repeat(pad);
276
+ }
277
+
278
+ /**
279
+ * Draw a horizontal line
280
+ */
281
+ function hLine(width = 60) {
282
+ return c.dark(ch.h.repeat(width));
283
+ }
284
+
285
+ /**
286
+ * Play the rocket launch animation
287
+ */
288
+ async function playRocketAnimation() {
289
+ hideCursor();
290
+ clearScreen();
291
+
292
+ const cols = process.stdout.columns || 80;
293
+ const centerX = Math.floor(cols / 2) - 12;
294
+
295
+ // Show pre-launch text
296
+ moveTo(2, centerX - 5);
297
+ process.stdout.write(c.dim('Initializing Upfyn-Code...'));
298
+
299
+ await sleep(400);
300
+
301
+ // Play each frame
302
+ const delays = [500, 300, 200, 150, 120, 120, 150, 200, 400];
303
+
304
+ for (let f = 0; f < ROCKET_FRAMES.length; f++) {
305
+ // Clear the animation area (rows 4-14)
306
+ for (let row = 4; row <= 14; row++) {
307
+ moveTo(row, 1);
308
+ process.stdout.write(' '.repeat(cols));
309
+ }
310
+
311
+ const frame = ROCKET_FRAMES[f];
312
+ for (let i = 0; i < frame.length; i++) {
313
+ moveTo(4 + i, centerX);
314
+ process.stdout.write(frame[i]);
315
+ }
316
+
317
+ // Status text under animation
318
+ moveTo(14, centerX - 2);
319
+ if (f === 0) process.stdout.write(c.dim('Systems check...'));
320
+ else if (f === 1) process.stdout.write(c.orange('Ignition sequence started'));
321
+ else if (f === 2) process.stdout.write(c.yellow('Lift-off!'));
322
+ else if (f <= 4) process.stdout.write(c.bright('Ascending...'));
323
+ else if (f <= 6) process.stdout.write(c.sky('Reaching orbit...'));
324
+ else if (f === 7) process.stdout.write(c.cyan('Entering space...'));
325
+ else process.stdout.write(c.bBright('Ready to code! *'));
326
+
327
+ await sleep(delays[f] || 200);
328
+ }
329
+
330
+ await sleep(300);
331
+ showCursor();
332
+ }
333
+
334
+ /**
335
+ * Draw a box with title (terminal-safe characters)
336
+ */
337
+ function box(title, content, width = 58) {
338
+ const lines = [];
339
+ const inner = width - 4;
340
+
341
+ lines.push(` ${t.dimGray}${ch.tl}${ch.h}${r} ${c.bBright(title)} ${t.dimGray}${ch.h.repeat(Math.max(0, inner - title.length - 1))}${ch.tr}${r}`);
342
+
343
+ for (const line of content) {
344
+ lines.push(` ${t.dimGray}${ch.v}${r} ${line}`);
345
+ }
346
+
347
+ lines.push(` ${t.dimGray}${ch.bl}${ch.h.repeat(width - 2)}${ch.br}${r}`);
348
+ return lines;
349
+ }
350
+
351
+ /**
352
+ * Show the main welcome screen (Claude Code-style layout)
353
+ */
354
+ function showWelcomeScreen(version, options = {}) {
355
+ const cols = process.stdout.columns || 80;
356
+ const { serverUrl, username, cwd, isConnected } = options;
357
+ const W = Math.min(cols - 4, 72); // content width
358
+ const leftW = 32;
359
+ const rightW = W - leftW - 3; // 3 for separator + padding
360
+
361
+ clearScreen();
362
+
363
+ const lines = [];
364
+
365
+ // --- Top border with version ---
366
+ const verStr = `Upfyn-Code v${version}`;
367
+ const topPad = Math.max(0, W - verStr.length - 4);
368
+ lines.push('');
369
+ lines.push(` ${t.dimGray}${ch.h}${ch.h} ${r}${c.bBright(verStr)}${t.dimGray} ${ch.h.repeat(topPad)}${r}`);
370
+ lines.push('');
371
+
372
+ // --- Build left and right panels ---
373
+ const leftPanel = [];
374
+ const rightPanel = [];
375
+
376
+ // Left: Welcome + Rocket mascot
377
+ leftPanel.push(c.bWhite(' Welcome back!'));
378
+ leftPanel.push('');
379
+ leftPanel.push(` ${t.dimGray}|${r}`);
380
+ leftPanel.push(` ${t.white}/${t.brightBlue}^${t.white}\\${r}`);
381
+ leftPanel.push(` ${t.white}|${t.blue}|${t.brightBlue}o${t.blue}|${t.white}|${r}`);
382
+ leftPanel.push(` ${t.white}|${t.blue}||${t.white}|${r}`);
383
+ leftPanel.push(` ${t.white}/${t.blue}===${t.white}\\${r}`);
384
+ leftPanel.push(` ${t.cyan}~~~~~${r}`);
385
+ leftPanel.push('');
386
+ if (username) {
387
+ leftPanel.push(` ${c.gray('User:')} ${c.bright(username)}`);
388
+ }
389
+ leftPanel.push(` ${c.gray('Path:')} ${c.dim(cwd || process.cwd())}`);
390
+
391
+ // Right: Tips + Recent Activity
392
+ rightPanel.push(` ${c.bCyan('Tips for getting started')}`);
393
+ rightPanel.push(` ${c.gray('Run')} ${c.bright('uc connect')} ${c.gray('to bridge to web UI')}`);
394
+ rightPanel.push(` ${c.gray('Run')} ${c.bright('uc start')} ${c.gray('to launch local server')}`);
395
+ rightPanel.push(` ${c.gray('Run')} ${c.bright('uc status')} ${c.gray('to see configuration')}`);
396
+ rightPanel.push(` ${c.gray('Run')} ${c.bright('uc help')} ${c.gray('for all commands')}`);
397
+ rightPanel.push('');
398
+ rightPanel.push(` ${c.bCyan('Recent activity')}`);
399
+ if (isConnected) {
400
+ rightPanel.push(` ${c.green('*')} Connected to ${c.bright(serverUrl || 'server')}`);
401
+ } else {
402
+ rightPanel.push(` ${c.dim('No active connection')}`);
403
+ }
404
+ rightPanel.push('');
405
+ if (serverUrl) {
406
+ rightPanel.push(` ${c.gray('Server:')} ${c.cyan(serverUrl)}`);
407
+ }
408
+
409
+ // Merge panels side by side
410
+ const maxRows = Math.max(leftPanel.length, rightPanel.length);
411
+
412
+ for (let i = 0; i < maxRows; i++) {
413
+ const left = padAnsi(leftPanel[i] || '', leftW);
414
+ const right = rightPanel[i] || '';
415
+ lines.push(`${left}${t.dimGray}${ch.v}${r} ${right}`);
416
+ }
417
+
418
+ lines.push('');
419
+
420
+ // --- Bottom border ---
421
+ lines.push(` ${t.dimGray}${ch.h.repeat(W)}${r}`);
422
+ lines.push('');
423
+
424
+ // --- Status bar ---
425
+ const statusItems = [];
426
+ if (isConnected) {
427
+ statusItems.push(`${t.green}* Connected${r}`);
428
+ } else {
429
+ statusItems.push(`${t.dimGray}o Not connected${r}`);
430
+ }
431
+ statusItems.push(`${t.gray}v${version}${r}`);
432
+
433
+ lines.push(` ${statusItems.join(` ${t.dimGray}${ch.dot}${r} `)}`);
434
+ lines.push('');
435
+
436
+ // --- Input prompt area (between two lines like Claude Code) ---
437
+ lines.push(` ${t.dimGray}${ch.h.repeat(W)}${r}`);
438
+ lines.push(` ${t.brightBlue}>${r} ${c.dim('Type a command or use /upfynai-connect ...')}`);
439
+ lines.push(` ${t.dimGray}${ch.h.repeat(W)}${r}`);
440
+ lines.push('');
441
+
442
+ process.stdout.write(lines.join('\n') + '\n');
443
+ }
444
+
445
+ /**
446
+ * Show connection success banner
447
+ */
448
+ function showConnectionBanner(username, serverUrl) {
449
+ const W = 50;
450
+ const lines = [];
451
+ const bLine = (content) => ` ${t.green}${ch.v}${r}${padAnsi(content, W)}${t.green}${ch.v}${r}`;
452
+ lines.push('');
453
+ lines.push(` ${t.green}${ch.tl}${ch.h.repeat(W)}${ch.tr}${r}`);
454
+ lines.push(bLine(` ${c.green('OK')} ${c.bWhite('Connected successfully!')}`));
455
+ lines.push(bLine(''));
456
+ lines.push(bLine(` ${c.gray('User:')} ${c.bright(username || 'Unknown')}`));
457
+ lines.push(bLine(` ${c.gray('Server:')} ${c.cyan(serverUrl || 'Unknown')}`));
458
+ lines.push(bLine(''));
459
+ lines.push(bLine(` ${c.dim('Your machine is bridged to the web UI.')}`));
460
+ lines.push(bLine(` ${c.dim('Press Ctrl+C to disconnect.')}`));
461
+ lines.push(` ${t.green}${ch.bl}${ch.h.repeat(W)}${ch.br}${r}`);
462
+ lines.push('');
463
+
464
+ process.stdout.write(lines.join('\n') + '\n');
465
+ }
466
+
467
+ /**
468
+ * Show a spinner with message
469
+ */
470
+ function createSpinner(message) {
471
+ const frames = ['/', '-', '\\', '|'];
472
+ let i = 0;
473
+ let interval;
474
+
475
+ return {
476
+ start() {
477
+ hideCursor();
478
+ interval = setInterval(() => {
479
+ const frame = frames[i % frames.length];
480
+ process.stdout.write(`\r ${t.brightBlue}${frame}${r} ${c.gray(message)}`);
481
+ i++;
482
+ }, 100);
483
+ },
484
+ stop(finalMessage) {
485
+ clearInterval(interval);
486
+ process.stdout.write(`\r ${c.green('OK')} ${c.white(finalMessage || message)}${''.padEnd(20)}\n`);
487
+ showCursor();
488
+ },
489
+ fail(finalMessage) {
490
+ clearInterval(interval);
491
+ process.stdout.write(`\r ${c.red('FAIL')} ${c.red(finalMessage || message)}${''.padEnd(20)}\n`);
492
+ showCursor();
493
+ }
494
+ };
495
+ }
496
+
497
+ /**
498
+ * Show styled help
499
+ */
500
+ function showStyledHelp(version) {
501
+ const W = 60;
502
+ const lines = [];
503
+
504
+ lines.push('');
505
+ // Header box — build each line to exactly W visible chars inside the borders
506
+ const hdrLine1 = ` ${c.bBright('Upfyn-Code')} ${c.dim('--')} ${c.gray('by Thinqmesh Technologies')}`;
507
+ const hdrLine2 = ` ${c.dim('Visual AI Coding Interface with Upfyn-Canvas')}`;
508
+ lines.push(` ${t.brightBlue}${ch.tl}${ch.h.repeat(W)}${ch.tr}${r}`);
509
+ lines.push(` ${t.brightBlue}${ch.v}${r}${padAnsi(hdrLine1, W)}${t.brightBlue}${ch.v}${r}`);
510
+ lines.push(` ${t.brightBlue}${ch.v}${r}${padAnsi(hdrLine2, W)}${t.brightBlue}${ch.v}${r}`);
511
+ lines.push(` ${t.brightBlue}${ch.bl}${ch.h.repeat(W)}${ch.br}${r}`);
512
+ lines.push('');
513
+
514
+ // Usage
515
+ lines.push(` ${c.bBright('Usage:')}`);
516
+ lines.push(` ${c.bright('uc')} ${c.cyan('[command]')} ${c.dim('[options]')}`);
517
+ lines.push('');
518
+
519
+ // Commands
520
+ lines.push(` ${c.bBright('Commands:')}`);
521
+ const cmds = [
522
+ ['(default)', 'Launch Claude Code with Upfyn branding'],
523
+ ['start', 'Start the local server only'],
524
+ ['connect', 'Connect local machine to hosted server'],
525
+ ['config', 'View/set configuration (API key, etc.)'],
526
+ ['status', 'Show configuration and data locations'],
527
+ ['install-commands', 'Install /upfynai-* slash commands'],
528
+ ['uninstall-commands', 'Remove /upfynai-* slash commands'],
529
+ ['update', 'Update to the latest version'],
530
+ ['help', 'Show this help information'],
531
+ ['version', 'Show version information'],
532
+ ];
533
+ for (const [cmd, desc] of cmds) {
534
+ lines.push(` ${c.cyan(cmd.padEnd(22))} ${c.gray(desc)}`);
535
+ }
536
+ lines.push('');
537
+
538
+ // Slash Commands
539
+ lines.push(` ${c.bBright('Slash Commands')} ${c.dim('(inside Claude Code CLI):')}`);
540
+ const slashCmds = [
541
+ ['/upfynai', 'Start the web UI server'],
542
+ ['/upfynai-connect', 'Connect CLI session to web UI'],
543
+ ['/upfynai-disconnect', 'Disconnect from web UI'],
544
+ ['/upfynai-status', 'Show connection status'],
545
+ ['/upfynai-doctor', 'Run diagnostics'],
546
+ ];
547
+ for (const [cmd, desc] of slashCmds) {
548
+ lines.push(` ${c.violet(cmd.padEnd(22))} ${c.gray(desc)}`);
549
+ }
550
+ lines.push('');
551
+
552
+ // Options
553
+ lines.push(` ${c.bBright('Options:')}`);
554
+ const opts = [
555
+ ['-p, --port <port>', 'Set server port (default: 3001)'],
556
+ ['--server <url>', 'Server URL for connect'],
557
+ ['--key <token>', 'Relay token for connect'],
558
+ ['--api-key <key>', 'Set Anthropic API key'],
559
+ ['--database-path <path>', 'Custom database location'],
560
+ ['-h, --help', 'Show help'],
561
+ ['-v, --version', 'Show version'],
562
+ ];
563
+ for (const [opt, desc] of opts) {
564
+ lines.push(` ${c.yellow(opt.padEnd(28))} ${c.gray(desc)}`);
565
+ }
566
+ lines.push('');
567
+
568
+ // Examples
569
+ lines.push(` ${c.bBright('Examples:')}`);
570
+ lines.push(` ${c.dim('$')} ${c.bright('uc')} ${c.dim('# Launch Claude Code')}`);
571
+ lines.push(` ${c.dim('$')} ${c.bright('uc connect --key upfyn_xxx')} ${c.dim('# Bridge to hosted server')}`);
572
+ lines.push(` ${c.dim('$')} ${c.bright('uc config --api-key sk-ant-xxx')} ${c.dim('# Save Anthropic API key')}`);
573
+ lines.push(` ${c.dim('$')} ${c.bright('uc start --port 8080')} ${c.dim('# Start on port 8080')}`);
574
+ lines.push(` ${c.dim('$')} ${c.bright('uc status')} ${c.dim('# Show configuration')}`);
575
+ lines.push('');
576
+
577
+ // Footer
578
+ lines.push(` ${c.dim('Documentation:')} ${c.cyan('https://cli.upfyn.com/docs')}`);
579
+ lines.push(` ${c.dim('Report Issues:')} ${c.cyan('https://github.com/thinqmesh/upfynai-code/issues')}`);
580
+ lines.push('');
581
+
582
+ process.stdout.write(lines.join('\n') + '\n');
583
+ }
584
+
585
+ /**
586
+ * Show styled status
587
+ */
588
+ function showStyledStatus(info) {
589
+ const lines = [];
590
+
591
+ lines.push('');
592
+ lines.push(` ${c.bBright('Upfyn-Code')} ${c.dim('-- Status')}`);
593
+ lines.push(` ${c.dark('='.repeat(50))}`);
594
+ lines.push('');
595
+
596
+ // Version
597
+ lines.push(` ${c.bright('Version')} ${c.white(info.version)}`);
598
+ lines.push('');
599
+
600
+ // Installation
601
+ lines.push(` ${c.bright('Install')} ${c.dim(info.installDir)}`);
602
+ lines.push(` ${c.bright('Database')} ${c.dim(info.dbPath)}`);
603
+ if (info.dbExists) {
604
+ lines.push(` ${c.green('OK')} ${c.gray(`Exists (${info.dbSize})`)}`);
605
+ } else {
606
+ lines.push(` ${c.yellow('--')} ${c.gray('Not created yet')}`);
607
+ }
608
+ lines.push('');
609
+
610
+ // Configuration
611
+ lines.push(` ${c.bright('Config')}`);
612
+ lines.push(` ${c.gray('PORT')} ${c.cyan(info.port)} ${info.portDefault ? c.dim('(default)') : ''}`);
613
+ lines.push(` ${c.gray('DATABASE')} ${c.dim(info.dbPath)}`);
614
+ lines.push(` ${c.gray('CLI')} ${c.dim(info.claudeCli || 'claude (default)')}`);
615
+ if (info.apiKey) {
616
+ lines.push(` ${c.gray('API KEY')} ${c.green('Set')} ${c.dim('(sk-ant-...' + info.apiKey.slice(-4) + ')')}`);
617
+ }
618
+ lines.push('');
619
+
620
+ // Bottom
621
+ lines.push(` ${c.dark('='.repeat(50))}`);
622
+ lines.push('');
623
+ lines.push(` ${c.bCyan('Tips:')}`);
624
+ lines.push(` ${c.dim('>')} Use ${c.bright('uc')} to launch Claude Code with Upfyn branding`);
625
+ lines.push(` ${c.dim('>')} Use ${c.bright('uc connect --key upfyn_xxx')} to bridge to web UI`);
626
+ lines.push(` ${c.dim('>')} Use ${c.bright('uc config --api-key sk-ant-xxx')} to save your API key`);
627
+ lines.push('');
628
+
629
+ process.stdout.write(lines.join('\n') + '\n');
630
+ }
631
+
632
+ /**
633
+ * Show server startup banner
634
+ */
635
+ function showServerBanner(port, version) {
636
+ const lines = [];
637
+
638
+ lines.push('');
639
+ for (const line of LOGO) lines.push(` ${line}`);
640
+ lines.push('');
641
+ lines.push(` ${c.dim('v' + version)} ${c.dim('--')} ${c.gray('by Thinqmesh Technologies')}`);
642
+ lines.push('');
643
+ lines.push(` ${c.dark(ch.h.repeat(52))}`);
644
+ lines.push('');
645
+ lines.push(` ${c.green('*')} ${c.bWhite('Server running')} ${c.gray('on')} ${c.bCyan(`http://localhost:${port}`)}`);
646
+ lines.push('');
647
+ lines.push(` ${c.dim('Press Ctrl+C to stop.')}`);
648
+ lines.push('');
649
+
650
+ process.stdout.write(lines.join('\n') + '\n');
651
+ }
652
+
653
+ /**
654
+ * Show connect startup with animation
655
+ */
656
+ async function showConnectStartup(serverUrl, machine, user, version) {
657
+ await playRocketAnimation();
658
+
659
+ clearScreen();
660
+
661
+ const lines = [];
662
+
663
+ lines.push('');
664
+ for (const line of LOGO) lines.push(` ${line}`);
665
+ lines.push('');
666
+ lines.push(` ${c.dim('v' + version)} ${c.dim('--')} ${c.gray('Relay Client')}`);
667
+ lines.push('');
668
+ lines.push(` ${c.dark(ch.h.repeat(52))}`);
669
+ lines.push('');
670
+ lines.push(` ${c.gray('Server:')} ${c.cyan(serverUrl)}`);
671
+ lines.push(` ${c.gray('Machine:')} ${c.dim(machine)}`);
672
+ lines.push(` ${c.gray('User:')} ${c.dim(user)}`);
673
+ lines.push('');
674
+
675
+ process.stdout.write(lines.join('\n') + '\n');
676
+ }
677
+
678
+ /**
679
+ * Show interactive launch screen (before spawning Claude Code)
680
+ */
681
+ function showLaunchScreen(version, claudeBin, options = {}) {
682
+ const cols = process.stdout.columns || 80;
683
+ const W = Math.min(cols - 4, 72);
684
+
685
+ const lines = [];
686
+
687
+ lines.push('');
688
+ for (const line of LOGO) lines.push(` ${line}`);
689
+ lines.push('');
690
+ lines.push(` ${c.dim('v' + version)} ${c.dim('--')} ${c.gray('by Thinqmesh Technologies')}`);
691
+ lines.push('');
692
+ lines.push(` ${c.dark(ch.h.repeat(W))}`);
693
+ lines.push('');
694
+
695
+ if (claudeBin) {
696
+ lines.push(` ${c.green('*')} ${c.bWhite('Claude Code detected:')} ${c.dim(claudeBin)}`);
697
+ }
698
+ if (options.relayStatus) {
699
+ lines.push(` ${c.green('*')} ${c.bWhite('Relay:')} ${c.dim(options.relayStatus)}`);
700
+ }
701
+ if (options.apiKey) {
702
+ lines.push(` ${c.green('*')} ${c.bWhite('API Key:')} ${c.dim('Configured')}`);
703
+ }
704
+ lines.push('');
705
+
706
+ if (claudeBin) {
707
+ lines.push(` ${c.dim('Launching Claude Code...')}`);
708
+ lines.push(` ${c.dim('Slash commands available: /upfynai-connect, /upfynai-status')}`);
709
+ } else {
710
+ lines.push(` ${c.yellow('!')} ${c.white('Claude Code CLI not found on PATH')}`);
711
+ lines.push('');
712
+ lines.push(` ${c.gray('Install Claude Code:')}`);
713
+ lines.push(` ${c.bright('npm install -g @anthropic-ai/claude-code')}`);
714
+ lines.push('');
715
+ lines.push(` ${c.gray('Or start the server instead:')}`);
716
+ lines.push(` ${c.bright('uc start')}`);
717
+ }
718
+ lines.push('');
719
+ lines.push(` ${c.dark(ch.h.repeat(W))}`);
720
+ lines.push('');
721
+
722
+ process.stdout.write(lines.join('\n') + '\n');
723
+ }
724
+
725
+ /**
726
+ * Show config display
727
+ */
728
+ function showConfig(config) {
729
+ const lines = [];
730
+ lines.push('');
731
+ lines.push(` ${c.bBright('Upfyn-Code')} ${c.dim('-- Configuration')}`);
732
+ lines.push(` ${c.dark('='.repeat(50))}`);
733
+ lines.push('');
734
+
735
+ lines.push(` ${c.gray('Server')} ${config.server ? c.cyan(config.server) : c.dim('(not set)')}`);
736
+ lines.push(` ${c.gray('Relay Key')} ${config.relayKey ? c.green('Set') + ' ' + c.dim('(' + config.relayKey.slice(0, 10) + '...)') : c.dim('(not set)')}`);
737
+ lines.push(` ${c.gray('API Key')} ${config.anthropicApiKey ? c.green('Set') + ' ' + c.dim('(sk-ant-...' + config.anthropicApiKey.slice(-4) + ')') : c.dim('(not set)')}`);
738
+ lines.push('');
739
+ lines.push(` ${c.gray('Config file:')} ${c.dim(config._path || '~/.upfynai/config.json')}`);
740
+ lines.push('');
741
+
742
+ lines.push(` ${c.bCyan('Set values:')}`);
743
+ lines.push(` ${c.dim('$')} ${c.bright('uc config --api-key sk-ant-xxx')} ${c.dim('# Save Anthropic API key')}`);
744
+ lines.push(` ${c.dim('$')} ${c.bright('uc config --clear-api-key')} ${c.dim('# Remove API key')}`);
745
+ lines.push(` ${c.dim('$')} ${c.bright('uc connect --server <url> --key <token>')}`);
746
+ lines.push('');
747
+
748
+ process.stdout.write(lines.join('\n') + '\n');
749
+ }
750
+
751
+ /**
752
+ * Log a relay event with icon
753
+ */
754
+ function logRelayEvent(icon, message, color = 'gray') {
755
+ const colorFn = c[color] || c.gray;
756
+ const timestamp = new Date().toLocaleTimeString('en-US', { hour12: false });
757
+ console.log(` ${c.dim(timestamp)} ${icon} ${colorFn(message)}`);
758
+ }
759
+
760
+ export {
761
+ theme,
762
+ c,
763
+ ch,
764
+ LOGO,
765
+ detectTerminalSupport,
766
+ playRocketAnimation,
767
+ showWelcomeScreen,
768
+ showConnectionBanner,
769
+ showStyledHelp,
770
+ showStyledStatus,
771
+ showServerBanner,
772
+ showConnectStartup,
773
+ showLaunchScreen,
774
+ showConfig,
775
+ logRelayEvent,
776
+ createSpinner,
777
+ hideCursor,
778
+ showCursor,
779
+ clearScreen,
780
+ sleep,
781
+ hLine,
782
+ box,
783
+ stripAnsi,
784
+ padAnsi,
785
+ };