upfynai-code 2.3.0 → 2.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 (162) 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 +128 -66
  116. package/client/dist/manifest.json +61 -15
  117. package/client/dist/sw.js +19 -55
  118. package/commands/upfynai-connect.md +31 -18
  119. package/commands/upfynai.md +45 -26
  120. package/package.json +1 -1
  121. package/server/cli-ui.js +320 -169
  122. package/server/cli.js +255 -23
  123. package/server/constants/config.js +29 -3
  124. package/server/database/auth.db +0 -0
  125. package/server/index.js +380 -121
  126. package/server/mcp-server.js +2 -1
  127. package/server/middleware/auth.js +18 -8
  128. package/server/openrouter.js +137 -0
  129. package/server/relay-client.js +262 -18
  130. package/server/routes/agent.js +54 -19
  131. package/server/routes/auth.js +23 -12
  132. package/server/routes/commands.js +1 -1
  133. package/server/routes/settings.js +91 -0
  134. package/shared/modelConstants.js +29 -0
  135. package/client/dist/assets/AppContent-CTSHQdyq.js +0 -513
  136. package/client/dist/assets/CanvasPanel-Cig0Mo9s.js +0 -6
  137. package/client/dist/assets/LoginModal-silya-zP.js +0 -11
  138. package/client/dist/assets/MarkdownPreview-B3c7OEj6.js +0 -1
  139. package/client/dist/assets/channel-CSnvHe_M.js +0 -1
  140. package/client/dist/assets/classDiagram-v2-f2320105-xtym7GEZ.js +0 -2
  141. package/client/dist/assets/clone-B75abXxS.js +0 -1
  142. package/client/dist/assets/flowDiagram-66a62f08-tffoET0H.js +0 -4
  143. package/client/dist/assets/flowDiagram-v2-96b9c2cf-Byc3JCHh.js +0 -1
  144. package/client/dist/assets/index-BnXuHrpJ.js +0 -523
  145. package/client/dist/assets/index-BwxNox94.css +0 -1
  146. package/client/dist/assets/index-D1urGMYu.js +0 -95
  147. package/client/dist/assets/mindmap-definition-fc14e90a-BOOrexmz.js +0 -415
  148. package/client/dist/assets/pdf-TYrZqVzP.js +0 -12
  149. package/client/dist/assets/percentages-BXMCSKIN-C9GT0OD3.js +0 -199
  150. package/client/dist/assets/pica-VkdyTzi8.js +0 -2
  151. package/client/dist/assets/roundRect-mAH3dD0p.js +0 -1
  152. package/client/dist/assets/subset-worker.chunk-C8QUSruZ.js +0 -1
  153. package/client/dist/assets/vendor-codemirror-BARtJV1V.js +0 -16
  154. package/client/dist/assets/vendor-codemirror-langs-52_y1wip.js +0 -20
  155. package/client/dist/assets/vendor-i18n-ByAl-gdx.js +0 -1
  156. package/client/dist/assets/vendor-icons-D33IkSIf.js +0 -1
  157. package/client/dist/assets/vendor-react-CHoMc7ka.js +0 -8
  158. package/client/dist/assets/vendor-xterm-DBb3RXlu.js +0 -66
  159. package/client/dist/assets/vendor-xterm-DrlLKa8f.css +0 -1
  160. package/client/dist/llms.txt +0 -40
  161. package/client/dist/robots.txt +0 -11
  162. package/client/dist/sitemap.xml +0 -45
package/server/cli.js CHANGED
@@ -6,9 +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
11
  * connect - Connect to hosted server (relay bridge)
12
+ * config - View/set configuration (API key, server, etc.)
12
13
  * status - Show configuration and data locations
13
14
  * help - Show help information
14
15
  * version - Show version information
@@ -19,6 +20,7 @@ import path from 'path';
19
20
  import os from 'os';
20
21
  import { fileURLToPath } from 'url';
21
22
  import { dirname } from 'path';
23
+ import { execSync, spawn } from 'child_process';
22
24
  import {
23
25
  c,
24
26
  showStyledHelp,
@@ -26,8 +28,12 @@ import {
26
28
  showServerBanner,
27
29
  showConnectStartup,
28
30
  showConnectionBanner,
31
+ showLaunchScreen,
32
+ showConfig,
29
33
  logRelayEvent,
30
34
  createSpinner,
35
+ playRocketAnimation,
36
+ clearScreen,
31
37
  } from './cli-ui.js';
32
38
 
33
39
  const __filename = fileURLToPath(import.meta.url);
@@ -37,6 +43,9 @@ const __dirname = dirname(__filename);
37
43
  const packageJsonPath = path.join(__dirname, '../package.json');
38
44
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
39
45
 
46
+ const CONFIG_DIR = path.join(os.homedir(), '.upfynai');
47
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
48
+
40
49
  // Load environment variables from .env file if it exists
41
50
  function loadEnvFile() {
42
51
  try {
@@ -56,6 +65,26 @@ function loadEnvFile() {
56
65
  }
57
66
  }
58
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
+
59
88
  // Get the database path (same logic as db.js)
60
89
  function getDatabasePath() {
61
90
  loadEnvFile();
@@ -67,8 +96,29 @@ function getInstallDir() {
67
96
  return path.join(__dirname, '..');
68
97
  }
69
98
 
70
- // Show status command styled
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
+ }
118
+
119
+ // --- Show status command ---
71
120
  function showStatus() {
121
+ const config = loadConfig();
72
122
  const installDir = getInstallDir();
73
123
  const dbPath = getDatabasePath();
74
124
  const dbExists = fs.existsSync(dbPath);
@@ -86,11 +136,12 @@ function showStatus() {
86
136
  dbSize,
87
137
  port: process.env.PORT || '3001',
88
138
  portDefault: !process.env.PORT,
89
- claudeCli: process.env.CLAUDE_CLI_PATH || null,
139
+ claudeCli: findClaudeBinary() || null,
140
+ apiKey: config.anthropicApiKey || null,
90
141
  });
91
142
  }
92
143
 
93
- // Show help — styled
144
+ // Show help
94
145
  function showHelp() {
95
146
  showStyledHelp(packageJson.version);
96
147
  }
@@ -114,18 +165,17 @@ function isNewerVersion(v1, v2) {
114
165
  // Check for updates
115
166
  async function checkForUpdates(silent = false) {
116
167
  try {
117
- const { execSync } = await import('child_process');
118
168
  const latestVersion = execSync('npm show upfynai-code version', { encoding: 'utf8' }).trim();
119
169
  const currentVersion = packageJson.version;
120
170
 
121
171
  if (isNewerVersion(latestVersion, currentVersion)) {
122
172
  if (!silent) {
123
- console.log(`\n ${c.yellow('')} New version available: ${c.bBright(latestVersion)} ${c.dim(`(current: ${currentVersion})`)}`);
173
+ console.log(`\n ${c.yellow('!')} New version available: ${c.bBright(latestVersion)} ${c.dim(`(current: ${currentVersion})`)}`);
124
174
  console.log(` Run ${c.bright('uc update')} to update\n`);
125
175
  }
126
176
  return { hasUpdate: true, latestVersion, currentVersion };
127
177
  } else if (!silent) {
128
- console.log(` ${c.green('')} You are on the latest version (${currentVersion})`);
178
+ console.log(` ${c.green('OK')} You are on the latest version (${currentVersion})`);
129
179
  }
130
180
  return { hasUpdate: false, latestVersion, currentVersion };
131
181
  } catch (e) {
@@ -139,7 +189,6 @@ async function checkForUpdates(silent = false) {
139
189
  // Update the package
140
190
  async function updatePackage() {
141
191
  try {
142
- const { execSync } = await import('child_process');
143
192
  const spinner = createSpinner('Checking for updates...');
144
193
  spinner.start();
145
194
 
@@ -148,20 +197,20 @@ async function updatePackage() {
148
197
  spinner.stop(`Already on the latest version (${currentVersion})`);
149
198
  return;
150
199
  }
151
- spinner.stop(`Update available: ${currentVersion} ${latestVersion}`);
200
+ spinner.stop(`Update available: ${currentVersion} -> ${latestVersion}`);
152
201
 
153
202
  const installSpinner = createSpinner(`Updating to v${latestVersion}...`);
154
203
  installSpinner.start();
155
204
  execSync('npm update -g upfynai-code', { stdio: 'pipe' });
156
205
  installSpinner.stop(`Updated to v${latestVersion}! Restart uc to use the new version.`);
157
206
  } catch (e) {
158
- console.log(` ${c.red('')} Update failed: ${e.message}`);
207
+ console.log(` ${c.red('FAIL')} Update failed: ${e.message}`);
159
208
  console.log(` ${c.dim('Try running manually:')} ${c.bright('npm update -g upfynai-code')}`);
160
209
  }
161
210
  }
162
211
 
163
212
  // Install slash commands to ~/.claude/commands/
164
- async function installCommands() {
213
+ async function installCommands(silent = false) {
165
214
  const commandsSource = path.join(__dirname, '..', 'commands');
166
215
  const commandsDest = path.join(os.homedir(), '.claude', 'commands');
167
216
 
@@ -170,8 +219,8 @@ async function installCommands() {
170
219
  }
171
220
 
172
221
  if (!fs.existsSync(commandsSource)) {
173
- console.log(` ${c.red('')} Commands directory not found`);
174
- process.exit(1);
222
+ if (!silent) console.log(` ${c.red('FAIL')} Commands directory not found`);
223
+ return 0;
175
224
  }
176
225
 
177
226
  const files = fs.readdirSync(commandsSource).filter(f => f.endsWith('.md'));
@@ -182,12 +231,17 @@ async function installCommands() {
182
231
  path.join(commandsSource, file),
183
232
  path.join(commandsDest, file)
184
233
  );
185
- console.log(` ${c.green('✓')} Installed ${c.violet('/' + file.replace('.md', ''))}`);
234
+ if (!silent) {
235
+ console.log(` ${c.green('OK')} Installed ${c.violet('/' + file.replace('.md', ''))}`);
236
+ }
186
237
  count++;
187
238
  }
188
239
 
189
- console.log(`\n ${c.bBright(`${count} slash commands installed!`)}`);
190
- 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;
191
245
  }
192
246
 
193
247
  // Remove slash commands from ~/.claude/commands/
@@ -204,28 +258,189 @@ async function uninstallCommands() {
204
258
 
205
259
  for (const file of files) {
206
260
  fs.unlinkSync(path.join(commandsDest, file));
207
- console.log(` ${c.green('')} Removed ${c.violet('/' + file.replace('.md', ''))}`);
261
+ console.log(` ${c.green('OK')} Removed ${c.violet('/' + file.replace('.md', ''))}`);
208
262
  }
209
263
 
210
264
  console.log(`\n ${c.bBright(`${files.length} slash commands removed.`)}\n`);
211
265
  }
212
266
 
213
- // Start the server
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;
279
+ }
280
+
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 the local web UI server in the background
329
+ const port = process.env.PORT || '3001';
330
+ process.env.VITE_IS_PLATFORM = 'true'; // local mode
331
+ startBackgroundServer(port);
332
+
333
+ // 9. Start background relay if config exists (for cloud mode)
334
+ if (config.relayKey && config.server) {
335
+ startBackgroundRelay(config);
336
+ }
337
+
338
+ // 10. Spawn Claude Code interactively
339
+ const child = spawn(claudeBin, [], {
340
+ stdio: 'inherit',
341
+ cwd: process.cwd(),
342
+ shell: true,
343
+ env: childEnv,
344
+ });
345
+
346
+ child.on('exit', (code) => {
347
+ process.exit(code || 0);
348
+ });
349
+
350
+ child.on('error', (err) => {
351
+ console.log(`\n ${c.red('FAIL')} Failed to launch Claude Code: ${err.message}`);
352
+ console.log(` ${c.dim('Make sure Claude Code is installed:')}`);
353
+ console.log(` ${c.bright('npm install -g @anthropic-ai/claude-code')}\n`);
354
+ process.exit(1);
355
+ });
356
+ }
357
+
358
+ // --- Background server for local mode ---
359
+ function startBackgroundServer(port) {
360
+ // Start the server in the background so the web UI is available
361
+ import('./index.js').then(() => {
362
+ // Server started successfully in background
363
+ }).catch(() => {
364
+ // Server failed to start — user still has Claude Code CLI
365
+ });
366
+
367
+ // Open browser after a short delay
368
+ setTimeout(() => {
369
+ const url = `http://localhost:${port}`;
370
+ try {
371
+ const openCmd = process.platform === 'win32' ? 'start'
372
+ : process.platform === 'darwin' ? 'open'
373
+ : 'xdg-open';
374
+ execSync(`${openCmd} ${url}`, { stdio: 'ignore' });
375
+ } catch {
376
+ // Browser open failed — not critical
377
+ }
378
+ }, 2000);
379
+ }
380
+
381
+ // --- Background relay connection ---
382
+ function startBackgroundRelay(config) {
383
+ // Import and start relay in background (non-blocking)
384
+ import('./relay-client.js').then(({ connectToServerBackground }) => {
385
+ if (typeof connectToServerBackground === 'function') {
386
+ connectToServerBackground({
387
+ server: config.server,
388
+ key: config.relayKey,
389
+ silent: true,
390
+ });
391
+ }
392
+ }).catch(() => {
393
+ // Relay module not available or no background connect — that's ok
394
+ });
395
+ }
396
+
397
+ // Start the server (self-hosted local mode)
214
398
  async function startServer() {
215
399
  // Check for updates silently on startup
216
400
  checkForUpdates(true);
217
401
 
218
- // Show server banner
219
402
  const port = process.env.PORT || '3001';
403
+
404
+ // Auto-detect local mode — set IS_PLATFORM flag
405
+ if (!process.env.RAILWAY_ENVIRONMENT && !process.env.VERCEL && !process.env.FORCE_HOSTED_MODE) {
406
+ process.env.VITE_IS_PLATFORM = 'true';
407
+ }
408
+
409
+ // Show server banner
220
410
  showServerBanner(port, packageJson.version);
221
411
 
412
+ // Detect local agents
413
+ const claudeBin = findClaudeBinary();
414
+ if (claudeBin) {
415
+ console.log(` ${c.green('OK')} Claude Code detected: ${c.bright(claudeBin)}`);
416
+ } else {
417
+ console.log(` ${c.yellow('!')} Claude Code not found. Install: ${c.bright('npm i -g @anthropic-ai/claude-code')}`);
418
+ }
419
+ console.log('');
420
+
222
421
  // Import and run the server
223
422
  await import('./index.js');
423
+
424
+ // Auto-open browser after server starts (local mode only)
425
+ if (!process.env.RAILWAY_ENVIRONMENT && !process.env.VERCEL) {
426
+ const url = `http://localhost:${port}`;
427
+ setTimeout(() => {
428
+ try {
429
+ const openCmd = process.platform === 'win32' ? 'start'
430
+ : process.platform === 'darwin' ? 'open'
431
+ : 'xdg-open';
432
+ execSync(`${openCmd} ${url}`, { stdio: 'ignore' });
433
+ console.log(` ${c.green('OK')} Opened ${c.cyan(url)} in browser\n`);
434
+ } catch {
435
+ console.log(` ${c.dim('Open in browser:')} ${c.cyan(url)}\n`);
436
+ }
437
+ }, 1500);
438
+ }
224
439
  }
225
440
 
226
441
  // Parse CLI arguments
227
442
  function parseArgs(args) {
228
- const parsed = { command: 'start', options: {} };
443
+ const parsed = { command: null, options: {} };
229
444
 
230
445
  for (let i = 0; i < args.length; i++) {
231
446
  const arg = args[i];
@@ -246,15 +461,26 @@ function parseArgs(args) {
246
461
  parsed.options.key = args[++i];
247
462
  } else if (arg.startsWith('--key=')) {
248
463
  parsed.options.key = arg.split('=')[1];
464
+ } else if (arg === '--api-key') {
465
+ parsed.options.apiKey = args[++i];
466
+ } else if (arg.startsWith('--api-key=')) {
467
+ parsed.options.apiKey = arg.split('=')[1];
468
+ } else if (arg === '--clear-api-key') {
469
+ parsed.options.clearApiKey = true;
249
470
  } else if (arg === '--help' || arg === '-h') {
250
471
  parsed.command = 'help';
251
472
  } else if (arg === '--version' || arg === '-v') {
252
473
  parsed.command = 'version';
253
- } else if (!arg.startsWith('-')) {
474
+ } else if (!arg.startsWith('-') && !parsed.command) {
254
475
  parsed.command = arg;
255
476
  }
256
477
  }
257
478
 
479
+ // Default command: launch interactive
480
+ if (!parsed.command) {
481
+ parsed.command = 'launch';
482
+ }
483
+
258
484
  return parsed;
259
485
  }
260
486
 
@@ -272,6 +498,9 @@ async function main() {
272
498
  }
273
499
 
274
500
  switch (command) {
501
+ case 'launch':
502
+ await launchInteractive();
503
+ break;
275
504
  case 'start':
276
505
  await startServer();
277
506
  break;
@@ -292,6 +521,9 @@ async function main() {
292
521
  case 'update':
293
522
  await updatePackage();
294
523
  break;
524
+ case 'config':
525
+ handleConfig(options);
526
+ break;
295
527
  case 'install-commands':
296
528
  await installCommands();
297
529
  break;
@@ -307,7 +539,7 @@ async function main() {
307
539
  break;
308
540
  }
309
541
  default:
310
- console.error(`\n ${c.red('')} Unknown command: ${command}`);
542
+ console.error(`\n ${c.red('FAIL')} Unknown command: ${command}`);
311
543
  console.log(` Run ${c.bright('"uc help"')} for usage information.\n`);
312
544
  process.exit(1);
313
545
  }
@@ -315,6 +547,6 @@ async function main() {
315
547
 
316
548
  // Run the CLI
317
549
  main().catch(error => {
318
- console.error(`\n ${c.red('')} Error: ${error.message}`);
550
+ console.error(`\n ${c.red('FAIL')} Error: ${error.message}`);
319
551
  process.exit(1);
320
552
  });
@@ -1,5 +1,31 @@
1
1
  /**
2
- * Environment Flag: Is Platform
3
- * Indicates if the app is running in Platform mode (hosted) or OSS mode (self-hosted)
2
+ * Environment Flag: Is Platform (Self-Hosted / Local Mode)
3
+ *
4
+ * When true, the app runs in single-user local mode:
5
+ * - Skips JWT authentication (uses first DB user)
6
+ * - Claude Code SDK runs directly on the machine
7
+ * - No relay connection needed
8
+ *
9
+ * Auto-detected when:
10
+ * - VITE_IS_PLATFORM=true is set, OR
11
+ * - Running locally (not on Railway/Vercel/cloud)
4
12
  */
5
- export const IS_PLATFORM = process.env.VITE_IS_PLATFORM === 'true';
13
+ const isCloudEnv = !!(
14
+ process.env.RAILWAY_ENVIRONMENT ||
15
+ process.env.VERCEL ||
16
+ process.env.RENDER ||
17
+ process.env.FLY_APP_NAME ||
18
+ process.env.HEROKU_APP_NAME
19
+ );
20
+
21
+ export const IS_PLATFORM = process.env.VITE_IS_PLATFORM === 'true' || (!isCloudEnv && !process.env.FORCE_HOSTED_MODE);
22
+
23
+ /**
24
+ * True when running on a cloud provider (Railway, Vercel, etc.)
25
+ */
26
+ export const IS_CLOUD = isCloudEnv;
27
+
28
+ /**
29
+ * True when running locally (self-hosted mode)
30
+ */
31
+ export const IS_LOCAL = IS_PLATFORM && !isCloudEnv;
Binary file