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
package/server/cli.js CHANGED
@@ -6,8 +6,10 @@
6
6
  * Visual AI coding interface with Upfyn-Canvas for AI coding assistants
7
7
  *
8
8
  * Commands:
9
- * (no args) - Start the server (default)
9
+ * (no args) - Launch Claude Code with Upfyn branding (default)
10
10
  * start - Start the server
11
+ * connect - Connect to hosted server (relay bridge)
12
+ * config - View/set configuration (API key, server, etc.)
11
13
  * status - Show configuration and data locations
12
14
  * help - Show help information
13
15
  * version - Show version information
@@ -18,41 +20,32 @@ import path from 'path';
18
20
  import os from 'os';
19
21
  import { fileURLToPath } from 'url';
20
22
  import { dirname } from 'path';
23
+ import { execSync, spawn } from 'child_process';
24
+ import {
25
+ c,
26
+ showStyledHelp,
27
+ showStyledStatus,
28
+ showServerBanner,
29
+ showConnectStartup,
30
+ showConnectionBanner,
31
+ showLaunchScreen,
32
+ showConfig,
33
+ logRelayEvent,
34
+ createSpinner,
35
+ playRocketAnimation,
36
+ clearScreen,
37
+ } from './cli-ui.js';
21
38
 
22
39
  const __filename = fileURLToPath(import.meta.url);
23
40
  const __dirname = dirname(__filename);
24
41
 
25
- // ANSI color codes for terminal output
26
- const colors = {
27
- reset: '\x1b[0m',
28
- bright: '\x1b[1m',
29
- dim: '\x1b[2m',
30
-
31
- // Foreground colors
32
- cyan: '\x1b[36m',
33
- green: '\x1b[32m',
34
- yellow: '\x1b[33m',
35
- blue: '\x1b[34m',
36
- magenta: '\x1b[35m',
37
- white: '\x1b[37m',
38
- gray: '\x1b[90m',
39
- };
40
-
41
- // Helper to colorize text
42
- const c = {
43
- info: (text) => `${colors.cyan}${text}${colors.reset}`,
44
- ok: (text) => `${colors.green}${text}${colors.reset}`,
45
- warn: (text) => `${colors.yellow}${text}${colors.reset}`,
46
- error: (text) => `${colors.yellow}${text}${colors.reset}`,
47
- tip: (text) => `${colors.blue}${text}${colors.reset}`,
48
- bright: (text) => `${colors.bright}${text}${colors.reset}`,
49
- dim: (text) => `${colors.dim}${text}${colors.reset}`,
50
- };
51
-
52
42
  // Load package.json for version info
53
43
  const packageJsonPath = path.join(__dirname, '../package.json');
54
44
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
55
45
 
46
+ const CONFIG_DIR = path.join(os.homedir(), '.upfynai');
47
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
48
+
56
49
  // Load environment variables from .env file if it exists
57
50
  function loadEnvFile() {
58
51
  try {
@@ -72,6 +65,26 @@ function loadEnvFile() {
72
65
  }
73
66
  }
74
67
 
68
+ // Load saved config from ~/.upfynai/config.json
69
+ function loadConfig() {
70
+ try {
71
+ if (fs.existsSync(CONFIG_FILE)) {
72
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
73
+ }
74
+ } catch (e) { /* ignore */ }
75
+ return {};
76
+ }
77
+
78
+ // Save config to ~/.upfynai/config.json
79
+ function saveConfig(config) {
80
+ if (!fs.existsSync(CONFIG_DIR)) {
81
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
82
+ }
83
+ // Don't save internal keys
84
+ const { _path, ...rest } = config;
85
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(rest, null, 2));
86
+ }
87
+
75
88
  // Get the database path (same logic as db.js)
76
89
  function getDatabasePath() {
77
90
  loadEnvFile();
@@ -83,123 +96,54 @@ function getInstallDir() {
83
96
  return path.join(__dirname, '..');
84
97
  }
85
98
 
86
- // Show status command
87
- function showStatus() {
88
- console.log(`\n${c.bright('Upfyn-Code - Status')}\n`);
89
- console.log(c.dim(''.repeat(60)));
90
-
91
- // Version info
92
- console.log(`\n${c.info('[INFO]')} Version: ${c.bright(packageJson.version)}`);
99
+ // --- OS Detection + Claude Binary Discovery ---
100
+
101
+ function findClaudeBinary() {
102
+ const isWindows = process.platform === 'win32';
103
+ const candidates = isWindows
104
+ ? ['claude.exe', 'claude.cmd', 'claude']
105
+ : ['claude'];
106
+
107
+ for (const cmd of candidates) {
108
+ try {
109
+ const whichCmd = isWindows ? 'where' : 'which';
110
+ const result = execSync(`${whichCmd} ${cmd}`, { stdio: 'pipe', encoding: 'utf8' }).trim();
111
+ if (result) return cmd;
112
+ } catch {
113
+ // not found, try next
114
+ }
115
+ }
116
+ return null;
117
+ }
93
118
 
94
- // Installation location
119
+ // --- Show status command ---
120
+ function showStatus() {
121
+ const config = loadConfig();
95
122
  const installDir = getInstallDir();
96
- console.log(`\n${c.info('[INFO]')} Installation Directory:`);
97
- console.log(` ${c.dim(installDir)}`);
98
-
99
- // Database location
100
123
  const dbPath = getDatabasePath();
101
124
  const dbExists = fs.existsSync(dbPath);
102
- console.log(`\n${c.info('[INFO]')} Database Location:`);
103
- console.log(` ${c.dim(dbPath)}`);
104
- console.log(` Status: ${dbExists ? c.ok('[OK] Exists') : c.warn('[WARN] Not created yet (will be created on first run)')}`);
105
-
125
+ let dbSize = '';
106
126
  if (dbExists) {
107
127
  const stats = fs.statSync(dbPath);
108
- console.log(` Size: ${c.dim((stats.size / 1024).toFixed(2) + ' KB')}`);
109
- console.log(` Modified: ${c.dim(stats.mtime.toLocaleString())}`);
128
+ dbSize = (stats.size / 1024).toFixed(2) + ' KB';
110
129
  }
111
130
 
112
- // Environment variables
113
- console.log(`\n${c.info('[INFO]')} Configuration:`);
114
- console.log(` PORT: ${c.bright(process.env.PORT || '3001')} ${c.dim(process.env.PORT ? '' : '(default)')}`);
115
- console.log(` DATABASE_PATH: ${c.dim(process.env.DATABASE_PATH || '(using default location)')}`);
116
- console.log(` CLAUDE_CLI_PATH: ${c.dim(process.env.CLAUDE_CLI_PATH || 'claude (default)')}`);
117
- console.log(` CONTEXT_WINDOW: ${c.dim(process.env.CONTEXT_WINDOW || '160000 (default)')}`);
118
-
119
- // Claude projects folder
120
- const claudeProjectsPath = path.join(os.homedir(), '.claude', 'projects');
121
- const projectsExists = fs.existsSync(claudeProjectsPath);
122
- console.log(`\n${c.info('[INFO]')} Claude Projects Folder:`);
123
- console.log(` ${c.dim(claudeProjectsPath)}`);
124
- console.log(` Status: ${projectsExists ? c.ok('[OK] Exists') : c.warn('[WARN] Not found')}`);
125
-
126
- // Config file location
127
- const envFilePath = path.join(__dirname, '../.env');
128
- const envExists = fs.existsSync(envFilePath);
129
- console.log(`\n${c.info('[INFO]')} Configuration File:`);
130
- console.log(` ${c.dim(envFilePath)}`);
131
- console.log(` Status: ${envExists ? c.ok('[OK] Exists') : c.warn('[WARN] Not found (using defaults)')}`);
132
-
133
- console.log('\n' + c.dim('═'.repeat(60)));
134
- console.log(`\n${c.tip('[TIP]')} Hints:`);
135
- console.log(` ${c.dim('>')} Use ${c.bright('uc --port 8080')} to run on a custom port`);
136
- console.log(` ${c.dim('>')} Use ${c.bright('uc --database-path /path/to/db')} for custom database`);
137
- console.log(` ${c.dim('>')} Run ${c.bright('uc help')} for all options`);
138
- console.log(` ${c.dim('>')} Access the UI at http://localhost:${process.env.PORT || '3001'}\n`);
131
+ showStyledStatus({
132
+ version: packageJson.version,
133
+ installDir,
134
+ dbPath,
135
+ dbExists,
136
+ dbSize,
137
+ port: process.env.PORT || '3001',
138
+ portDefault: !process.env.PORT,
139
+ claudeCli: findClaudeBinary() || null,
140
+ apiKey: config.anthropicApiKey || null,
141
+ });
139
142
  }
140
143
 
141
144
  // Show help
142
145
  function showHelp() {
143
- console.log(`
144
- ╔═══════════════════════════════════════════════════════════════╗
145
- ║ Upfyn-Code — by Thinqmesh Technologies ║
146
- ║ Visual AI Coding Interface with tldraw Canvas ║
147
- ╚═══════════════════════════════════════════════════════════════╝
148
-
149
- Usage:
150
- upfynai-code [command] [options]
151
- uc [command] [options]
152
-
153
- Commands:
154
- start Start the server (default)
155
- connect Connect local machine to hosted server (relay bridge)
156
- status Show configuration and data locations
157
- install-commands Install /upfynai-* slash commands to ~/.claude/commands/
158
- uninstall-commands Remove /upfynai-* slash commands
159
- update Update to the latest version
160
- help Show this help information
161
- version Show version information
162
-
163
- Slash Commands (inside your AI CLI):
164
- /upfynai Start the web UI server
165
- /upfynai-connect Connect CLI session to web UI
166
- /upfynai-disconnect Disconnect from web UI
167
- /upfynai-status Show connection status
168
- /upfynai-doctor Run diagnostics
169
- /upfynai-export Export session/canvas data
170
- /upfynai-stop Stop the web UI server
171
- /upfynai-local Full local installation setup
172
- /upfynai-uninstall Remove Upfyn-Code
173
-
174
- Options:
175
- -p, --port <port> Set server port (default: 3001)
176
- --database-path <path> Set custom database location
177
- -h, --help Show this help information
178
- -v, --version Show version information
179
-
180
- Options:
181
- --server <url> Server URL for connect (default: https://upfynai.thinqmesh.com)
182
- --key <token> Relay token for connect (get from Settings > Relay Tokens)
183
-
184
- Examples:
185
- $ uc # Start with defaults
186
- $ uc --port 8080 # Start on port 8080
187
- $ uc connect --key upfyn_xxx # Bridge to hosted server
188
- $ uc start --port 4000 # Explicit start command
189
- $ uc status # Show configuration
190
-
191
- Environment Variables:
192
- PORT Set server port (default: 3001)
193
- DATABASE_PATH Set custom database location
194
- CLAUDE_CLI_PATH Set custom Claude CLI path
195
- CONTEXT_WINDOW Set context window size (default: 160000)
196
-
197
- Documentation:
198
- ${packageJson.homepage || 'https://github.com/AnitChaudhry/UpfynAI-Code'}
199
-
200
- Report Issues:
201
- ${packageJson.bugs?.url || 'https://github.com/AnitChaudhry/UpfynAI-Code/issues'}
202
- `);
146
+ showStyledHelp(packageJson.version);
203
147
  }
204
148
 
205
149
  // Show version
@@ -221,21 +165,22 @@ function isNewerVersion(v1, v2) {
221
165
  // Check for updates
222
166
  async function checkForUpdates(silent = false) {
223
167
  try {
224
- const { execSync } = await import('child_process');
225
168
  const latestVersion = execSync('npm show upfynai-code version', { encoding: 'utf8' }).trim();
226
169
  const currentVersion = packageJson.version;
227
170
 
228
171
  if (isNewerVersion(latestVersion, currentVersion)) {
229
- console.log(`\n${c.warn('[UPDATE]')} New version available: ${c.bright(latestVersion)} (current: ${currentVersion})`);
230
- console.log(` Run ${c.bright('uc update')} to update\n`);
172
+ if (!silent) {
173
+ console.log(`\n ${c.yellow('!')} New version available: ${c.bBright(latestVersion)} ${c.dim(`(current: ${currentVersion})`)}`);
174
+ console.log(` Run ${c.bright('uc update')} to update\n`);
175
+ }
231
176
  return { hasUpdate: true, latestVersion, currentVersion };
232
177
  } else if (!silent) {
233
- console.log(`${c.ok('[OK]')} You are on the latest version (${currentVersion})`);
178
+ console.log(` ${c.green('OK')} You are on the latest version (${currentVersion})`);
234
179
  }
235
180
  return { hasUpdate: false, latestVersion, currentVersion };
236
181
  } catch (e) {
237
182
  if (!silent) {
238
- console.log(`${c.warn('[WARN]')} Could not check for updates`);
183
+ console.log(` ${c.yellow('!')} Could not check for updates`);
239
184
  }
240
185
  return { hasUpdate: false, error: e.message };
241
186
  }
@@ -244,27 +189,28 @@ async function checkForUpdates(silent = false) {
244
189
  // Update the package
245
190
  async function updatePackage() {
246
191
  try {
247
- const { execSync } = await import('child_process');
248
- console.log(`${c.info('[INFO]')} Checking for updates...`);
192
+ const spinner = createSpinner('Checking for updates...');
193
+ spinner.start();
249
194
 
250
195
  const { hasUpdate, latestVersion, currentVersion } = await checkForUpdates(true);
251
-
252
196
  if (!hasUpdate) {
253
- console.log(`${c.ok('[OK]')} Already on the latest version (${currentVersion})`);
197
+ spinner.stop(`Already on the latest version (${currentVersion})`);
254
198
  return;
255
199
  }
200
+ spinner.stop(`Update available: ${currentVersion} -> ${latestVersion}`);
256
201
 
257
- console.log(`${c.info('[INFO]')} Updating from ${currentVersion} to ${latestVersion}...`);
258
- execSync('npm update -g upfynai-code', { stdio: 'inherit' });
259
- console.log(`${c.ok('[OK]')} Update complete! Restart uc to use the new version.`);
202
+ const installSpinner = createSpinner(`Updating to v${latestVersion}...`);
203
+ installSpinner.start();
204
+ execSync('npm update -g upfynai-code', { stdio: 'pipe' });
205
+ installSpinner.stop(`Updated to v${latestVersion}! Restart uc to use the new version.`);
260
206
  } catch (e) {
261
- console.error(`${c.error('[ERROR]')} Update failed: ${e.message}`);
262
- console.log(`${c.tip('[TIP]')} Try running manually: npm update -g upfynai-code`);
207
+ console.log(` ${c.red('FAIL')} Update failed: ${e.message}`);
208
+ console.log(` ${c.dim('Try running manually:')} ${c.bright('npm update -g upfynai-code')}`);
263
209
  }
264
210
  }
265
211
 
266
212
  // Install slash commands to ~/.claude/commands/
267
- async function installCommands() {
213
+ async function installCommands(silent = false) {
268
214
  const commandsSource = path.join(__dirname, '..', 'commands');
269
215
  const commandsDest = path.join(os.homedir(), '.claude', 'commands');
270
216
 
@@ -273,8 +219,8 @@ async function installCommands() {
273
219
  }
274
220
 
275
221
  if (!fs.existsSync(commandsSource)) {
276
- console.error(`${c.error('[ERROR]')} Commands directory not found`);
277
- process.exit(1);
222
+ if (!silent) console.log(` ${c.red('FAIL')} Commands directory not found`);
223
+ return 0;
278
224
  }
279
225
 
280
226
  const files = fs.readdirSync(commandsSource).filter(f => f.endsWith('.md'));
@@ -285,12 +231,17 @@ async function installCommands() {
285
231
  path.join(commandsSource, file),
286
232
  path.join(commandsDest, file)
287
233
  );
288
- console.log(`${c.ok('[OK]')} Installed /${file.replace('.md', '')}`);
234
+ if (!silent) {
235
+ console.log(` ${c.green('OK')} Installed ${c.violet('/' + file.replace('.md', ''))}`);
236
+ }
289
237
  count++;
290
238
  }
291
239
 
292
- console.log(`\n${c.bright(`${count} slash commands installed!`)}`);
293
- console.log(`${c.dim('Available in your AI CLI as /upfynai-*')}\n`);
240
+ if (!silent) {
241
+ console.log(`\n ${c.bBright(`${count} slash commands installed!`)}`);
242
+ console.log(` ${c.dim('Available in your AI CLI as /upfynai-*')}\n`);
243
+ }
244
+ return count;
294
245
  }
295
246
 
296
247
  // Remove slash commands from ~/.claude/commands/
@@ -301,16 +252,118 @@ async function uninstallCommands() {
301
252
  : [];
302
253
 
303
254
  if (files.length === 0) {
304
- console.log(`${c.warn('[WARN]')} No Upfyn-Code commands found to remove`);
255
+ console.log(` ${c.yellow('!')} No Upfyn-Code commands found to remove`);
305
256
  return;
306
257
  }
307
258
 
308
259
  for (const file of files) {
309
260
  fs.unlinkSync(path.join(commandsDest, file));
310
- console.log(`${c.ok('[OK]')} Removed /${file.replace('.md', '')}`);
261
+ console.log(` ${c.green('OK')} Removed ${c.violet('/' + file.replace('.md', ''))}`);
262
+ }
263
+
264
+ console.log(`\n ${c.bBright(`${files.length} slash commands removed.`)}\n`);
265
+ }
266
+
267
+ // --- Config command ---
268
+ function handleConfig(options) {
269
+ const config = loadConfig();
270
+
271
+ if (options.apiKey) {
272
+ // Set API key
273
+ config.anthropicApiKey = options.apiKey;
274
+ saveConfig(config);
275
+ console.log(`\n ${c.green('OK')} ${c.white('Anthropic API key saved')}`);
276
+ console.log(` ${c.dim('Key:')} sk-ant-...${options.apiKey.slice(-4)}`);
277
+ console.log(` ${c.dim('Stored in:')} ${CONFIG_FILE}\n`);
278
+ return;
311
279
  }
312
280
 
313
- console.log(`\n${c.bright(`${files.length} slash commands removed.`)}\n`);
281
+ if (options.clearApiKey) {
282
+ delete config.anthropicApiKey;
283
+ saveConfig(config);
284
+ console.log(`\n ${c.green('OK')} ${c.white('Anthropic API key removed')}\n`);
285
+ return;
286
+ }
287
+
288
+ // Show current config
289
+ showConfig({ ...config, _path: CONFIG_FILE });
290
+ }
291
+
292
+ // --- Launch Interactive (default command) ---
293
+ async function launchInteractive() {
294
+ // 1. Play rocket animation
295
+ await playRocketAnimation();
296
+ clearScreen();
297
+
298
+ // 2. Ensure slash commands are installed (silent)
299
+ await installCommands(true);
300
+
301
+ // 3. Find Claude Code binary
302
+ const claudeBin = findClaudeBinary();
303
+
304
+ // 4. Load config for relay + API key
305
+ const config = loadConfig();
306
+
307
+ // 5. Show launch screen
308
+ showLaunchScreen(packageJson.version, claudeBin, {
309
+ relayStatus: config.relayKey && config.server
310
+ ? `Auto-connecting to ${config.server}`
311
+ : null,
312
+ apiKey: config.anthropicApiKey || null,
313
+ });
314
+
315
+ // 6. If Claude Code not found, fall back to server mode
316
+ if (!claudeBin) {
317
+ console.log(` ${c.dim('Falling back to server mode...')}\n`);
318
+ await startServer();
319
+ return;
320
+ }
321
+
322
+ // 7. Build environment for Claude Code
323
+ const childEnv = { ...process.env };
324
+ if (config.anthropicApiKey) {
325
+ childEnv.ANTHROPIC_API_KEY = config.anthropicApiKey;
326
+ }
327
+
328
+ // 8. Start background relay if config exists
329
+ if (config.relayKey && config.server) {
330
+ startBackgroundRelay(config);
331
+ }
332
+
333
+ // 9. Spawn Claude Code interactively
334
+ const child = spawn(claudeBin, [], {
335
+ stdio: 'inherit',
336
+ cwd: process.cwd(),
337
+ shell: true,
338
+ env: childEnv,
339
+ });
340
+
341
+ child.on('exit', (code) => {
342
+ process.exit(code || 0);
343
+ });
344
+
345
+ child.on('error', (err) => {
346
+ console.log(`\n ${c.red('FAIL')} Failed to launch Claude Code: ${err.message}`);
347
+ console.log(` ${c.dim('Make sure Claude Code is installed:')}`);
348
+ console.log(` ${c.bright('npm install -g @anthropic-ai/claude-code')}\n`);
349
+ process.exit(1);
350
+ });
351
+ }
352
+
353
+ // --- Background relay connection ---
354
+ function startBackgroundRelay(config) {
355
+ // Import and start relay in background (non-blocking)
356
+ import('./relay-client.js').then(({ connectToServerBackground }) => {
357
+ if (typeof connectToServerBackground === 'function') {
358
+ connectToServerBackground({
359
+ server: config.server,
360
+ key: config.relayKey,
361
+ silent: true,
362
+ });
363
+ }
364
+ }).catch(() => {
365
+ // Relay module not available or no background connect — that's ok
366
+ });
314
367
  }
315
368
 
316
369
  // Start the server
@@ -318,13 +371,17 @@ async function startServer() {
318
371
  // Check for updates silently on startup
319
372
  checkForUpdates(true);
320
373
 
374
+ // Show server banner
375
+ const port = process.env.PORT || '3001';
376
+ showServerBanner(port, packageJson.version);
377
+
321
378
  // Import and run the server
322
379
  await import('./index.js');
323
380
  }
324
381
 
325
382
  // Parse CLI arguments
326
383
  function parseArgs(args) {
327
- const parsed = { command: 'start', options: {} };
384
+ const parsed = { command: null, options: {} };
328
385
 
329
386
  for (let i = 0; i < args.length; i++) {
330
387
  const arg = args[i];
@@ -345,15 +402,26 @@ function parseArgs(args) {
345
402
  parsed.options.key = args[++i];
346
403
  } else if (arg.startsWith('--key=')) {
347
404
  parsed.options.key = arg.split('=')[1];
405
+ } else if (arg === '--api-key') {
406
+ parsed.options.apiKey = args[++i];
407
+ } else if (arg.startsWith('--api-key=')) {
408
+ parsed.options.apiKey = arg.split('=')[1];
409
+ } else if (arg === '--clear-api-key') {
410
+ parsed.options.clearApiKey = true;
348
411
  } else if (arg === '--help' || arg === '-h') {
349
412
  parsed.command = 'help';
350
413
  } else if (arg === '--version' || arg === '-v') {
351
414
  parsed.command = 'version';
352
- } else if (!arg.startsWith('-')) {
415
+ } else if (!arg.startsWith('-') && !parsed.command) {
353
416
  parsed.command = arg;
354
417
  }
355
418
  }
356
419
 
420
+ // Default command: launch interactive
421
+ if (!parsed.command) {
422
+ parsed.command = 'launch';
423
+ }
424
+
357
425
  return parsed;
358
426
  }
359
427
 
@@ -371,6 +439,9 @@ async function main() {
371
439
  }
372
440
 
373
441
  switch (command) {
442
+ case 'launch':
443
+ await launchInteractive();
444
+ break;
374
445
  case 'start':
375
446
  await startServer();
376
447
  break;
@@ -391,6 +462,9 @@ async function main() {
391
462
  case 'update':
392
463
  await updatePackage();
393
464
  break;
465
+ case 'config':
466
+ handleConfig(options);
467
+ break;
394
468
  case 'install-commands':
395
469
  await installCommands();
396
470
  break;
@@ -406,14 +480,14 @@ async function main() {
406
480
  break;
407
481
  }
408
482
  default:
409
- console.error(`\n Unknown command: ${command}`);
410
- console.log(' Run "uc help" for usage information.\n');
483
+ console.error(`\n ${c.red('FAIL')} Unknown command: ${command}`);
484
+ console.log(` Run ${c.bright('"uc help"')} for usage information.\n`);
411
485
  process.exit(1);
412
486
  }
413
487
  }
414
488
 
415
489
  // Run the CLI
416
490
  main().catch(error => {
417
- console.error('\n❌ Error:', error.message);
491
+ console.error(`\n ${c.red('FAIL')} Error: ${error.message}`);
418
492
  process.exit(1);
419
493
  });