@nac3/forge-cli 0.2.0-alpha.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 (561) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +371 -0
  3. package/dist/bin/yf.d.ts +5 -0
  4. package/dist/bin/yf.d.ts.map +1 -0
  5. package/dist/bin/yf.js +86 -0
  6. package/dist/bin/yf.js.map +1 -0
  7. package/dist/chat/claude.d.ts +100 -0
  8. package/dist/chat/claude.d.ts.map +1 -0
  9. package/dist/chat/claude.js +228 -0
  10. package/dist/chat/claude.js.map +1 -0
  11. package/dist/chat/ingest_session.d.ts +97 -0
  12. package/dist/chat/ingest_session.d.ts.map +1 -0
  13. package/dist/chat/ingest_session.js +99 -0
  14. package/dist/chat/ingest_session.js.map +1 -0
  15. package/dist/chat/panel.d.ts +15 -0
  16. package/dist/chat/panel.d.ts.map +1 -0
  17. package/dist/chat/panel.js +1526 -0
  18. package/dist/chat/panel.js.map +1 -0
  19. package/dist/chat/persistence.d.ts +37 -0
  20. package/dist/chat/persistence.d.ts.map +1 -0
  21. package/dist/chat/persistence.js +91 -0
  22. package/dist/chat/persistence.js.map +1 -0
  23. package/dist/chat/server.d.ts +34 -0
  24. package/dist/chat/server.d.ts.map +1 -0
  25. package/dist/chat/server.js +1540 -0
  26. package/dist/chat/server.js.map +1 -0
  27. package/dist/chat/spec_extract.d.ts +35 -0
  28. package/dist/chat/spec_extract.d.ts.map +1 -0
  29. package/dist/chat/spec_extract.js +152 -0
  30. package/dist/chat/spec_extract.js.map +1 -0
  31. package/dist/chat/spec_plan.d.ts +65 -0
  32. package/dist/chat/spec_plan.d.ts.map +1 -0
  33. package/dist/chat/spec_plan.js +160 -0
  34. package/dist/chat/spec_plan.js.map +1 -0
  35. package/dist/chat/spec_scaffold.d.ts +95 -0
  36. package/dist/chat/spec_scaffold.d.ts.map +1 -0
  37. package/dist/chat/spec_scaffold.js +220 -0
  38. package/dist/chat/spec_scaffold.js.map +1 -0
  39. package/dist/chat/tools/git.d.ts +59 -0
  40. package/dist/chat/tools/git.d.ts.map +1 -0
  41. package/dist/chat/tools/git.js +313 -0
  42. package/dist/chat/tools/git.js.map +1 -0
  43. package/dist/chat/tools/github.d.ts +59 -0
  44. package/dist/chat/tools/github.d.ts.map +1 -0
  45. package/dist/chat/tools/github.js +310 -0
  46. package/dist/chat/tools/github.js.map +1 -0
  47. package/dist/chat/tools/lifecycle.d.ts +82 -0
  48. package/dist/chat/tools/lifecycle.d.ts.map +1 -0
  49. package/dist/chat/tools/lifecycle.js +295 -0
  50. package/dist/chat/tools/lifecycle.js.map +1 -0
  51. package/dist/chat/tools/manual.d.ts +26 -0
  52. package/dist/chat/tools/manual.d.ts.map +1 -0
  53. package/dist/chat/tools/manual.js +164 -0
  54. package/dist/chat/tools/manual.js.map +1 -0
  55. package/dist/chat/tools/reader.d.ts +80 -0
  56. package/dist/chat/tools/reader.d.ts.map +1 -0
  57. package/dist/chat/tools/reader.js +471 -0
  58. package/dist/chat/tools/reader.js.map +1 -0
  59. package/dist/chat/tools.d.ts +106 -0
  60. package/dist/chat/tools.d.ts.map +1 -0
  61. package/dist/chat/tools.js +587 -0
  62. package/dist/chat/tools.js.map +1 -0
  63. package/dist/codegen/e2e.d.ts +106 -0
  64. package/dist/codegen/e2e.d.ts.map +1 -0
  65. package/dist/codegen/e2e.js +931 -0
  66. package/dist/codegen/e2e.js.map +1 -0
  67. package/dist/codegen/v3_flow_emit.d.ts +70 -0
  68. package/dist/codegen/v3_flow_emit.d.ts.map +1 -0
  69. package/dist/codegen/v3_flow_emit.js +225 -0
  70. package/dist/codegen/v3_flow_emit.js.map +1 -0
  71. package/dist/commands/_stub.d.ts +2 -0
  72. package/dist/commands/_stub.d.ts.map +1 -0
  73. package/dist/commands/_stub.js +21 -0
  74. package/dist/commands/_stub.js.map +1 -0
  75. package/dist/commands/app.d.ts +31 -0
  76. package/dist/commands/app.d.ts.map +1 -0
  77. package/dist/commands/app.js +331 -0
  78. package/dist/commands/app.js.map +1 -0
  79. package/dist/commands/chat.d.ts +18 -0
  80. package/dist/commands/chat.d.ts.map +1 -0
  81. package/dist/commands/chat.js +76 -0
  82. package/dist/commands/chat.js.map +1 -0
  83. package/dist/commands/deploy.d.ts +21 -0
  84. package/dist/commands/deploy.d.ts.map +1 -0
  85. package/dist/commands/deploy.js +121 -0
  86. package/dist/commands/deploy.js.map +1 -0
  87. package/dist/commands/doctor.d.ts +14 -0
  88. package/dist/commands/doctor.d.ts.map +1 -0
  89. package/dist/commands/doctor.js +280 -0
  90. package/dist/commands/doctor.js.map +1 -0
  91. package/dist/commands/figma.d.ts +32 -0
  92. package/dist/commands/figma.d.ts.map +1 -0
  93. package/dist/commands/figma.js +141 -0
  94. package/dist/commands/figma.js.map +1 -0
  95. package/dist/commands/gen-flow-tests.d.ts +8 -0
  96. package/dist/commands/gen-flow-tests.d.ts.map +1 -0
  97. package/dist/commands/gen-flow-tests.js +78 -0
  98. package/dist/commands/gen-flow-tests.js.map +1 -0
  99. package/dist/commands/gen-tests.d.ts +9 -0
  100. package/dist/commands/gen-tests.d.ts.map +1 -0
  101. package/dist/commands/gen-tests.js +118 -0
  102. package/dist/commands/gen-tests.js.map +1 -0
  103. package/dist/commands/license.d.ts +14 -0
  104. package/dist/commands/license.d.ts.map +1 -0
  105. package/dist/commands/license.js +182 -0
  106. package/dist/commands/license.js.map +1 -0
  107. package/dist/commands/log.d.ts +19 -0
  108. package/dist/commands/log.d.ts.map +1 -0
  109. package/dist/commands/log.js +101 -0
  110. package/dist/commands/log.js.map +1 -0
  111. package/dist/commands/migrate.d.ts +118 -0
  112. package/dist/commands/migrate.d.ts.map +1 -0
  113. package/dist/commands/migrate.js +1410 -0
  114. package/dist/commands/migrate.js.map +1 -0
  115. package/dist/commands/mobile.d.ts +27 -0
  116. package/dist/commands/mobile.d.ts.map +1 -0
  117. package/dist/commands/mobile.js +90 -0
  118. package/dist/commands/mobile.js.map +1 -0
  119. package/dist/commands/new.d.ts +32 -0
  120. package/dist/commands/new.d.ts.map +1 -0
  121. package/dist/commands/new.js +107 -0
  122. package/dist/commands/new.js.map +1 -0
  123. package/dist/commands/pilot.d.ts +8 -0
  124. package/dist/commands/pilot.d.ts.map +1 -0
  125. package/dist/commands/pilot.js +104 -0
  126. package/dist/commands/pilot.js.map +1 -0
  127. package/dist/commands/projects.d.ts +21 -0
  128. package/dist/commands/projects.d.ts.map +1 -0
  129. package/dist/commands/projects.js +238 -0
  130. package/dist/commands/projects.js.map +1 -0
  131. package/dist/commands/publish.d.ts +35 -0
  132. package/dist/commands/publish.d.ts.map +1 -0
  133. package/dist/commands/publish.js +194 -0
  134. package/dist/commands/publish.js.map +1 -0
  135. package/dist/commands/repo.d.ts +59 -0
  136. package/dist/commands/repo.d.ts.map +1 -0
  137. package/dist/commands/repo.js +178 -0
  138. package/dist/commands/repo.js.map +1 -0
  139. package/dist/commands/review-screens.d.ts +28 -0
  140. package/dist/commands/review-screens.d.ts.map +1 -0
  141. package/dist/commands/review-screens.js +345 -0
  142. package/dist/commands/review-screens.js.map +1 -0
  143. package/dist/commands/scenarios.d.ts +23 -0
  144. package/dist/commands/scenarios.d.ts.map +1 -0
  145. package/dist/commands/scenarios.js +304 -0
  146. package/dist/commands/scenarios.js.map +1 -0
  147. package/dist/commands/ship.d.ts +18 -0
  148. package/dist/commands/ship.d.ts.map +1 -0
  149. package/dist/commands/ship.js +41 -0
  150. package/dist/commands/ship.js.map +1 -0
  151. package/dist/commands/test.d.ts +29 -0
  152. package/dist/commands/test.d.ts.map +1 -0
  153. package/dist/commands/test.js +62 -0
  154. package/dist/commands/test.js.map +1 -0
  155. package/dist/commands/tunnel.d.ts +22 -0
  156. package/dist/commands/tunnel.d.ts.map +1 -0
  157. package/dist/commands/tunnel.js +77 -0
  158. package/dist/commands/tunnel.js.map +1 -0
  159. package/dist/commands/validate.d.ts +14 -0
  160. package/dist/commands/validate.d.ts.map +1 -0
  161. package/dist/commands/validate.js +51 -0
  162. package/dist/commands/validate.js.map +1 -0
  163. package/dist/commands/vault.d.ts +32 -0
  164. package/dist/commands/vault.d.ts.map +1 -0
  165. package/dist/commands/vault.js +489 -0
  166. package/dist/commands/vault.js.map +1 -0
  167. package/dist/commands/voice.d.ts +16 -0
  168. package/dist/commands/voice.d.ts.map +1 -0
  169. package/dist/commands/voice.js +69 -0
  170. package/dist/commands/voice.js.map +1 -0
  171. package/dist/core/cascade_router.d.ts +90 -0
  172. package/dist/core/cascade_router.d.ts.map +1 -0
  173. package/dist/core/cascade_router.js +131 -0
  174. package/dist/core/cascade_router.js.map +1 -0
  175. package/dist/core/cf_tunnel.d.ts +52 -0
  176. package/dist/core/cf_tunnel.d.ts.map +1 -0
  177. package/dist/core/cf_tunnel.js +134 -0
  178. package/dist/core/cf_tunnel.js.map +1 -0
  179. package/dist/core/gha_dispatcher.d.ts +48 -0
  180. package/dist/core/gha_dispatcher.d.ts.map +1 -0
  181. package/dist/core/gha_dispatcher.js +198 -0
  182. package/dist/core/gha_dispatcher.js.map +1 -0
  183. package/dist/core/logger.d.ts +89 -0
  184. package/dist/core/logger.d.ts.map +1 -0
  185. package/dist/core/logger.js +245 -0
  186. package/dist/core/logger.js.map +1 -0
  187. package/dist/core/mode.d.ts +26 -0
  188. package/dist/core/mode.d.ts.map +1 -0
  189. package/dist/core/mode.js +122 -0
  190. package/dist/core/mode.js.map +1 -0
  191. package/dist/core/pairing.d.ts +40 -0
  192. package/dist/core/pairing.d.ts.map +1 -0
  193. package/dist/core/pairing.js +145 -0
  194. package/dist/core/pairing.js.map +1 -0
  195. package/dist/core/pilot_setup.d.ts +29 -0
  196. package/dist/core/pilot_setup.d.ts.map +1 -0
  197. package/dist/core/pilot_setup.js +119 -0
  198. package/dist/core/pilot_setup.js.map +1 -0
  199. package/dist/core/polar.d.ts +81 -0
  200. package/dist/core/polar.d.ts.map +1 -0
  201. package/dist/core/polar.js +175 -0
  202. package/dist/core/polar.js.map +1 -0
  203. package/dist/core/project_picker.d.ts +56 -0
  204. package/dist/core/project_picker.d.ts.map +1 -0
  205. package/dist/core/project_picker.js +86 -0
  206. package/dist/core/project_picker.js.map +1 -0
  207. package/dist/core/projects.d.ts +58 -0
  208. package/dist/core/projects.d.ts.map +1 -0
  209. package/dist/core/projects.js +146 -0
  210. package/dist/core/projects.js.map +1 -0
  211. package/dist/core/projects_sync.d.ts +80 -0
  212. package/dist/core/projects_sync.d.ts.map +1 -0
  213. package/dist/core/projects_sync.js +278 -0
  214. package/dist/core/projects_sync.js.map +1 -0
  215. package/dist/core/remote_runner.d.ts +70 -0
  216. package/dist/core/remote_runner.d.ts.map +1 -0
  217. package/dist/core/remote_runner.js +133 -0
  218. package/dist/core/remote_runner.js.map +1 -0
  219. package/dist/core/repo_state.d.ts +24 -0
  220. package/dist/core/repo_state.d.ts.map +1 -0
  221. package/dist/core/repo_state.js +109 -0
  222. package/dist/core/repo_state.js.map +1 -0
  223. package/dist/core/target.d.ts +31 -0
  224. package/dist/core/target.d.ts.map +1 -0
  225. package/dist/core/target.js +121 -0
  226. package/dist/core/target.js.map +1 -0
  227. package/dist/deploy/aws.d.ts +43 -0
  228. package/dist/deploy/aws.d.ts.map +1 -0
  229. package/dist/deploy/aws.js +173 -0
  230. package/dist/deploy/aws.js.map +1 -0
  231. package/dist/figma/api.d.ts +35 -0
  232. package/dist/figma/api.d.ts.map +1 -0
  233. package/dist/figma/api.js +40 -0
  234. package/dist/figma/api.js.map +1 -0
  235. package/dist/figma/decorator.d.ts +74 -0
  236. package/dist/figma/decorator.d.ts.map +1 -0
  237. package/dist/figma/decorator.js +210 -0
  238. package/dist/figma/decorator.js.map +1 -0
  239. package/dist/figma/heuristics.d.ts +29 -0
  240. package/dist/figma/heuristics.d.ts.map +1 -0
  241. package/dist/figma/heuristics.js +110 -0
  242. package/dist/figma/heuristics.js.map +1 -0
  243. package/dist/figma/normalize.d.ts +33 -0
  244. package/dist/figma/normalize.d.ts.map +1 -0
  245. package/dist/figma/normalize.js +101 -0
  246. package/dist/figma/normalize.js.map +1 -0
  247. package/dist/figma/tokens.d.ts +23 -0
  248. package/dist/figma/tokens.d.ts.map +1 -0
  249. package/dist/figma/tokens.js +111 -0
  250. package/dist/figma/tokens.js.map +1 -0
  251. package/dist/figma/types.d.ts +118 -0
  252. package/dist/figma/types.d.ts.map +1 -0
  253. package/dist/figma/types.js +12 -0
  254. package/dist/figma/types.js.map +1 -0
  255. package/dist/i18n/index.d.ts +48 -0
  256. package/dist/i18n/index.d.ts.map +1 -0
  257. package/dist/i18n/index.js +135 -0
  258. package/dist/i18n/index.js.map +1 -0
  259. package/dist/i18n/types.d.ts +52 -0
  260. package/dist/i18n/types.d.ts.map +1 -0
  261. package/dist/i18n/types.js +85 -0
  262. package/dist/i18n/types.js.map +1 -0
  263. package/dist/index.d.ts +12 -0
  264. package/dist/index.d.ts.map +1 -0
  265. package/dist/index.js +11 -0
  266. package/dist/index.js.map +1 -0
  267. package/dist/lan/mdns_packet.d.ts +74 -0
  268. package/dist/lan/mdns_packet.d.ts.map +1 -0
  269. package/dist/lan/mdns_packet.js +247 -0
  270. package/dist/lan/mdns_packet.js.map +1 -0
  271. package/dist/lan/mdns_service.d.ts +102 -0
  272. package/dist/lan/mdns_service.d.ts.map +1 -0
  273. package/dist/lan/mdns_service.js +206 -0
  274. package/dist/lan/mdns_service.js.map +1 -0
  275. package/dist/license/activate.d.ts +33 -0
  276. package/dist/license/activate.d.ts.map +1 -0
  277. package/dist/license/activate.js +135 -0
  278. package/dist/license/activate.js.map +1 -0
  279. package/dist/license/fingerprint.d.ts +2 -0
  280. package/dist/license/fingerprint.d.ts.map +1 -0
  281. package/dist/license/fingerprint.js +29 -0
  282. package/dist/license/fingerprint.js.map +1 -0
  283. package/dist/license/hito4_client.d.ts +24 -0
  284. package/dist/license/hito4_client.d.ts.map +1 -0
  285. package/dist/license/hito4_client.js +103 -0
  286. package/dist/license/hito4_client.js.map +1 -0
  287. package/dist/license/index.d.ts +22 -0
  288. package/dist/license/index.d.ts.map +1 -0
  289. package/dist/license/index.js +125 -0
  290. package/dist/license/index.js.map +1 -0
  291. package/dist/license/types.d.ts +38 -0
  292. package/dist/license/types.d.ts.map +1 -0
  293. package/dist/license/types.js +9 -0
  294. package/dist/license/types.js.map +1 -0
  295. package/dist/migrate/ai-apply.d.ts +198 -0
  296. package/dist/migrate/ai-apply.d.ts.map +1 -0
  297. package/dist/migrate/ai-apply.js +833 -0
  298. package/dist/migrate/ai-apply.js.map +1 -0
  299. package/dist/migrate/ai-decorator.d.ts +87 -0
  300. package/dist/migrate/ai-decorator.d.ts.map +1 -0
  301. package/dist/migrate/ai-decorator.js +203 -0
  302. package/dist/migrate/ai-decorator.js.map +1 -0
  303. package/dist/migrate/apply.d.ts +28 -0
  304. package/dist/migrate/apply.d.ts.map +1 -0
  305. package/dist/migrate/apply.js +119 -0
  306. package/dist/migrate/apply.js.map +1 -0
  307. package/dist/migrate/audit.d.ts +9 -0
  308. package/dist/migrate/audit.d.ts.map +1 -0
  309. package/dist/migrate/audit.js +197 -0
  310. package/dist/migrate/audit.js.map +1 -0
  311. package/dist/migrate/diff.d.ts +28 -0
  312. package/dist/migrate/diff.d.ts.map +1 -0
  313. package/dist/migrate/diff.js +154 -0
  314. package/dist/migrate/diff.js.map +1 -0
  315. package/dist/migrate/html-orchestrator.d.ts +81 -0
  316. package/dist/migrate/html-orchestrator.d.ts.map +1 -0
  317. package/dist/migrate/html-orchestrator.js +233 -0
  318. package/dist/migrate/html-orchestrator.js.map +1 -0
  319. package/dist/migrate/html-walker.d.ts +93 -0
  320. package/dist/migrate/html-walker.d.ts.map +1 -0
  321. package/dist/migrate/html-walker.js +288 -0
  322. package/dist/migrate/html-walker.js.map +1 -0
  323. package/dist/migrate/js-template-walker.d.ts +118 -0
  324. package/dist/migrate/js-template-walker.d.ts.map +1 -0
  325. package/dist/migrate/js-template-walker.js +644 -0
  326. package/dist/migrate/js-template-walker.js.map +1 -0
  327. package/dist/migrate/manifest-validator.d.ts +30 -0
  328. package/dist/migrate/manifest-validator.d.ts.map +1 -0
  329. package/dist/migrate/manifest-validator.js +261 -0
  330. package/dist/migrate/manifest-validator.js.map +1 -0
  331. package/dist/migrate/overrides.d.ts +58 -0
  332. package/dist/migrate/overrides.d.ts.map +1 -0
  333. package/dist/migrate/overrides.js +193 -0
  334. package/dist/migrate/overrides.js.map +1 -0
  335. package/dist/migrate/plugin-scope.d.ts +42 -0
  336. package/dist/migrate/plugin-scope.d.ts.map +1 -0
  337. package/dist/migrate/plugin-scope.js +94 -0
  338. package/dist/migrate/plugin-scope.js.map +1 -0
  339. package/dist/migrate/types.d.ts +45 -0
  340. package/dist/migrate/types.d.ts.map +1 -0
  341. package/dist/migrate/types.js +9 -0
  342. package/dist/migrate/types.js.map +1 -0
  343. package/dist/migrate/verb-inference.d.ts +37 -0
  344. package/dist/migrate/verb-inference.d.ts.map +1 -0
  345. package/dist/migrate/verb-inference.js +274 -0
  346. package/dist/migrate/verb-inference.js.map +1 -0
  347. package/dist/nac3/attrs.d.ts +87 -0
  348. package/dist/nac3/attrs.d.ts.map +1 -0
  349. package/dist/nac3/attrs.js +134 -0
  350. package/dist/nac3/attrs.js.map +1 -0
  351. package/dist/nac3/scenario_dsl.d.ts +71 -0
  352. package/dist/nac3/scenario_dsl.d.ts.map +1 -0
  353. package/dist/nac3/scenario_dsl.js +191 -0
  354. package/dist/nac3/scenario_dsl.js.map +1 -0
  355. package/dist/nac3/tokens.d.ts +126 -0
  356. package/dist/nac3/tokens.d.ts.map +1 -0
  357. package/dist/nac3/tokens.js +138 -0
  358. package/dist/nac3/tokens.js.map +1 -0
  359. package/dist/reader/parsers/csv.d.ts +42 -0
  360. package/dist/reader/parsers/csv.d.ts.map +1 -0
  361. package/dist/reader/parsers/csv.js +221 -0
  362. package/dist/reader/parsers/csv.js.map +1 -0
  363. package/dist/reader/parsers/docx.d.ts +31 -0
  364. package/dist/reader/parsers/docx.d.ts.map +1 -0
  365. package/dist/reader/parsers/docx.js +51 -0
  366. package/dist/reader/parsers/docx.js.map +1 -0
  367. package/dist/reader/parsers/epub.d.ts +39 -0
  368. package/dist/reader/parsers/epub.d.ts.map +1 -0
  369. package/dist/reader/parsers/epub.js +265 -0
  370. package/dist/reader/parsers/epub.js.map +1 -0
  371. package/dist/reader/parsers/html.d.ts +40 -0
  372. package/dist/reader/parsers/html.d.ts.map +1 -0
  373. package/dist/reader/parsers/html.js +386 -0
  374. package/dist/reader/parsers/html.js.map +1 -0
  375. package/dist/reader/parsers/md.d.ts +30 -0
  376. package/dist/reader/parsers/md.d.ts.map +1 -0
  377. package/dist/reader/parsers/md.js +199 -0
  378. package/dist/reader/parsers/md.js.map +1 -0
  379. package/dist/reader/parsers/pdf.d.ts +39 -0
  380. package/dist/reader/parsers/pdf.d.ts.map +1 -0
  381. package/dist/reader/parsers/pdf.js +220 -0
  382. package/dist/reader/parsers/pdf.js.map +1 -0
  383. package/dist/reader/parsers/rtf.d.ts +37 -0
  384. package/dist/reader/parsers/rtf.d.ts.map +1 -0
  385. package/dist/reader/parsers/rtf.js +347 -0
  386. package/dist/reader/parsers/rtf.js.map +1 -0
  387. package/dist/reader/parsers/source.d.ts +32 -0
  388. package/dist/reader/parsers/source.d.ts.map +1 -0
  389. package/dist/reader/parsers/source.js +122 -0
  390. package/dist/reader/parsers/source.js.map +1 -0
  391. package/dist/reader/parsers/txt.d.ts +25 -0
  392. package/dist/reader/parsers/txt.d.ts.map +1 -0
  393. package/dist/reader/parsers/txt.js +56 -0
  394. package/dist/reader/parsers/txt.js.map +1 -0
  395. package/dist/reader/parsers/xlsx.d.ts +33 -0
  396. package/dist/reader/parsers/xlsx.d.ts.map +1 -0
  397. package/dist/reader/parsers/xlsx.js +143 -0
  398. package/dist/reader/parsers/xlsx.js.map +1 -0
  399. package/dist/reader/registry.d.ts +39 -0
  400. package/dist/reader/registry.d.ts.map +1 -0
  401. package/dist/reader/registry.js +172 -0
  402. package/dist/reader/registry.js.map +1 -0
  403. package/dist/reader/search.d.ts +27 -0
  404. package/dist/reader/search.d.ts.map +1 -0
  405. package/dist/reader/search.js +77 -0
  406. package/dist/reader/search.js.map +1 -0
  407. package/dist/reader/state.d.ts +56 -0
  408. package/dist/reader/state.d.ts.map +1 -0
  409. package/dist/reader/state.js +179 -0
  410. package/dist/reader/state.js.map +1 -0
  411. package/dist/reader/types.d.ts +119 -0
  412. package/dist/reader/types.d.ts.map +1 -0
  413. package/dist/reader/types.js +23 -0
  414. package/dist/reader/types.js.map +1 -0
  415. package/dist/ship/run.d.ts +26 -0
  416. package/dist/ship/run.d.ts.map +1 -0
  417. package/dist/ship/run.js +123 -0
  418. package/dist/ship/run.js.map +1 -0
  419. package/dist/template/index.d.ts +50 -0
  420. package/dist/template/index.d.ts.map +1 -0
  421. package/dist/template/index.js +140 -0
  422. package/dist/template/index.js.map +1 -0
  423. package/dist/ui/colors.d.ts +13 -0
  424. package/dist/ui/colors.d.ts.map +1 -0
  425. package/dist/ui/colors.js +26 -0
  426. package/dist/ui/colors.js.map +1 -0
  427. package/dist/validate/index.d.ts +19 -0
  428. package/dist/validate/index.d.ts.map +1 -0
  429. package/dist/validate/index.js +181 -0
  430. package/dist/validate/index.js.map +1 -0
  431. package/dist/vault/catalog.d.ts +55 -0
  432. package/dist/vault/catalog.d.ts.map +1 -0
  433. package/dist/vault/catalog.js +424 -0
  434. package/dist/vault/catalog.js.map +1 -0
  435. package/dist/vault/crypto.d.ts +82 -0
  436. package/dist/vault/crypto.d.ts.map +1 -0
  437. package/dist/vault/crypto.js +173 -0
  438. package/dist/vault/crypto.js.map +1 -0
  439. package/dist/vault/git_askpass.d.ts +26 -0
  440. package/dist/vault/git_askpass.d.ts.map +1 -0
  441. package/dist/vault/git_askpass.js +104 -0
  442. package/dist/vault/git_askpass.js.map +1 -0
  443. package/dist/vault/migrator.d.ts +57 -0
  444. package/dist/vault/migrator.d.ts.map +1 -0
  445. package/dist/vault/migrator.js +204 -0
  446. package/dist/vault/migrator.js.map +1 -0
  447. package/dist/vault/redactor.d.ts +73 -0
  448. package/dist/vault/redactor.d.ts.map +1 -0
  449. package/dist/vault/redactor.js +182 -0
  450. package/dist/vault/redactor.js.map +1 -0
  451. package/dist/vault/store.d.ts +132 -0
  452. package/dist/vault/store.d.ts.map +1 -0
  453. package/dist/vault/store.js +335 -0
  454. package/dist/vault/store.js.map +1 -0
  455. package/dist/version.d.ts +8 -0
  456. package/dist/version.d.ts.map +1 -0
  457. package/dist/version.js +8 -0
  458. package/dist/version.js.map +1 -0
  459. package/dist/voice/chunker.d.ts +43 -0
  460. package/dist/voice/chunker.d.ts.map +1 -0
  461. package/dist/voice/chunker.js +133 -0
  462. package/dist/voice/chunker.js.map +1 -0
  463. package/dist/voice/config.d.ts +14 -0
  464. package/dist/voice/config.d.ts.map +1 -0
  465. package/dist/voice/config.js +51 -0
  466. package/dist/voice/config.js.map +1 -0
  467. package/dist/voice/intents.d.ts +71 -0
  468. package/dist/voice/intents.d.ts.map +1 -0
  469. package/dist/voice/intents.js +0 -0
  470. package/dist/voice/intents.js.map +1 -0
  471. package/dist/voice/providers/elevenlabs.d.ts +53 -0
  472. package/dist/voice/providers/elevenlabs.d.ts.map +1 -0
  473. package/dist/voice/providers/elevenlabs.js +159 -0
  474. package/dist/voice/providers/elevenlabs.js.map +1 -0
  475. package/dist/voice/providers/google.d.ts +56 -0
  476. package/dist/voice/providers/google.d.ts.map +1 -0
  477. package/dist/voice/providers/google.js +253 -0
  478. package/dist/voice/providers/google.js.map +1 -0
  479. package/dist/voice/providers/whisper.d.ts +44 -0
  480. package/dist/voice/providers/whisper.d.ts.map +1 -0
  481. package/dist/voice/providers/whisper.js +179 -0
  482. package/dist/voice/providers/whisper.js.map +1 -0
  483. package/dist/voice/registry.d.ts +35 -0
  484. package/dist/voice/registry.d.ts.map +1 -0
  485. package/dist/voice/registry.js +48 -0
  486. package/dist/voice/registry.js.map +1 -0
  487. package/dist/voice/router.d.ts +62 -0
  488. package/dist/voice/router.d.ts.map +1 -0
  489. package/dist/voice/router.js +175 -0
  490. package/dist/voice/router.js.map +1 -0
  491. package/dist/voice/types.d.ts +116 -0
  492. package/dist/voice/types.d.ts.map +1 -0
  493. package/dist/voice/types.js +22 -0
  494. package/dist/voice/types.js.map +1 -0
  495. package/dist/voice/voiceprint/enrollment.d.ts +36 -0
  496. package/dist/voice/voiceprint/enrollment.d.ts.map +1 -0
  497. package/dist/voice/voiceprint/enrollment.js +71 -0
  498. package/dist/voice/voiceprint/enrollment.js.map +1 -0
  499. package/dist/voice/voiceprint/identify.d.ts +16 -0
  500. package/dist/voice/voiceprint/identify.d.ts.map +1 -0
  501. package/dist/voice/voiceprint/identify.js +20 -0
  502. package/dist/voice/voiceprint/identify.js.map +1 -0
  503. package/dist/voice/voiceprint/liveness.d.ts +90 -0
  504. package/dist/voice/voiceprint/liveness.d.ts.map +1 -0
  505. package/dist/voice/voiceprint/liveness.js +251 -0
  506. package/dist/voice/voiceprint/liveness.js.map +1 -0
  507. package/dist/voice/voiceprint/match.d.ts +54 -0
  508. package/dist/voice/voiceprint/match.d.ts.map +1 -0
  509. package/dist/voice/voiceprint/match.js +88 -0
  510. package/dist/voice/voiceprint/match.js.map +1 -0
  511. package/dist/voice/voiceprint/providers/local-mfcc-stub.d.ts +44 -0
  512. package/dist/voice/voiceprint/providers/local-mfcc-stub.d.ts.map +1 -0
  513. package/dist/voice/voiceprint/providers/local-mfcc-stub.js +92 -0
  514. package/dist/voice/voiceprint/providers/local-mfcc-stub.js.map +1 -0
  515. package/dist/voice/voiceprint/store.d.ts +60 -0
  516. package/dist/voice/voiceprint/store.d.ts.map +1 -0
  517. package/dist/voice/voiceprint/store.js +155 -0
  518. package/dist/voice/voiceprint/store.js.map +1 -0
  519. package/dist/voice/voiceprint/trust.d.ts +90 -0
  520. package/dist/voice/voiceprint/trust.d.ts.map +1 -0
  521. package/dist/voice/voiceprint/trust.js +150 -0
  522. package/dist/voice/voiceprint/trust.js.map +1 -0
  523. package/dist/voice/voiceprint/types.d.ts +100 -0
  524. package/dist/voice/voiceprint/types.d.ts.map +1 -0
  525. package/dist/voice/voiceprint/types.js +23 -0
  526. package/dist/voice/voiceprint/types.js.map +1 -0
  527. package/dist/voice/wake.d.ts +64 -0
  528. package/dist/voice/wake.d.ts.map +1 -0
  529. package/dist/voice/wake.js +143 -0
  530. package/dist/voice/wake.js.map +1 -0
  531. package/docs/manuals/manual.ar.html +91 -0
  532. package/docs/manuals/manual.de.html +100 -0
  533. package/docs/manuals/manual.en.html +118 -0
  534. package/docs/manuals/manual.es.html +120 -0
  535. package/docs/manuals/manual.fr.html +102 -0
  536. package/docs/manuals/manual.hi.html +93 -0
  537. package/docs/manuals/manual.it.html +93 -0
  538. package/docs/manuals/manual.ja.html +97 -0
  539. package/docs/manuals/manual.pt.html +103 -0
  540. package/docs/manuals/manual.zh.html +89 -0
  541. package/package.json +94 -0
  542. package/src/i18n/catalogs/ar.json +86 -0
  543. package/src/i18n/catalogs/de.json +86 -0
  544. package/src/i18n/catalogs/en.json +86 -0
  545. package/src/i18n/catalogs/es.json +86 -0
  546. package/src/i18n/catalogs/fr.json +86 -0
  547. package/src/i18n/catalogs/hi.json +86 -0
  548. package/src/i18n/catalogs/it.json +86 -0
  549. package/src/i18n/catalogs/ja.json +86 -0
  550. package/src/i18n/catalogs/pt.json +86 -0
  551. package/src/i18n/catalogs/zh.json +86 -0
  552. package/templates/react-app/README.md +43 -0
  553. package/templates/react-app/index.html +12 -0
  554. package/templates/react-app/package.json +35 -0
  555. package/templates/react-app/src/App.tsx +106 -0
  556. package/templates/react-app/src/main.tsx +21 -0
  557. package/templates/react-app/src/nac/manifest.ts +46 -0
  558. package/templates/react-app/src/styles.css +68 -0
  559. package/templates/react-app/tsconfig.json +19 -0
  560. package/templates/react-app/vite.config.ts +12 -0
  561. package/templates/react-app/yujin.forge.json +15 -0
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Polar billing integration (PLAN connectivity #2 -- $10/mo).
3
+ *
4
+ * Polar.sh is the chosen payment processor: instant TC
5
+ * charge, no Stripe friction, ~3% + $0.30/transaction.
6
+ *
7
+ * Forge clients consult `prompts.yujin.app/api/v1/license`
8
+ * (HITO 4 territory) for current paid status. This module
9
+ * gives the local-side primitives:
10
+ *
11
+ * - Plan type definition (paid / free-disability / trial).
12
+ * - License cache + read/write to `~/.yujin-forge/license.json`.
13
+ * - URL generator for the Polar checkout flow.
14
+ * - validateLicense() probes the central server (eventual).
15
+ *
16
+ * Real signing + verification land when HITO 4 server
17
+ * exists. For now, the module ships:
18
+ * - in-memory + on-disk cache shape
19
+ * - the URL pattern that the chat panel surfaces when the
20
+ * user is on trial / expired
21
+ *
22
+ * ASCII-only.
23
+ */
24
+ import { promises as fs } from 'node:fs';
25
+ import path from 'node:path';
26
+ import os from 'node:os';
27
+ const LICENSE_FILE = 'license.json';
28
+ /** Polar checkout URL -- the live checkout link for the
29
+ * Yujin Forge $10/mo subscription. Env override stays so devs
30
+ * can point at a test link without rebuilding.
31
+ *
32
+ * Polar checkout links accept `?customer_external_id=<handle>`
33
+ * as a query param -- buildCheckoutUrl appends it -- and the
34
+ * value persists into the subscription metadata, which the
35
+ * HITO 4 webhook handler reads via
36
+ * metadata.customer_external_id (with email fallback). */
37
+ export const POLAR_CHECKOUT_BASE = process.env.YF_POLAR_CHECKOUT_URL ??
38
+ 'https://buy.polar.sh/polar_cl_hy6ndD0QpqBnJ8qM2HQPrxZekiIsijW1VQLIf2Y8AHa';
39
+ /** Build a Polar checkout URL with the user's handle as a
40
+ * metadata field so we can correlate the subscription back
41
+ * to their license. */
42
+ export function buildCheckoutUrl(userHandle) {
43
+ const url = new URL(POLAR_CHECKOUT_BASE);
44
+ if (userHandle)
45
+ url.searchParams.set('customer_external_id', userHandle);
46
+ return url.toString();
47
+ }
48
+ /** Path to ~/.yujin-forge/license.json. */
49
+ function licenseFile() {
50
+ return path.join(os.homedir(), '.yujin-forge', LICENSE_FILE);
51
+ }
52
+ /** Read the cached license. Returns a 'none' entry when no
53
+ * payment method has been captured yet -- the user is
54
+ * redirected to the Polar checkout. NO automatic trial
55
+ * seeding (TC-upfront model). */
56
+ export async function readLicense() {
57
+ const file = licenseFile();
58
+ try {
59
+ const raw = await fs.readFile(file, 'utf-8');
60
+ const parsed = JSON.parse(raw);
61
+ if (parsed && parsed.version === 1 && parsed.plan)
62
+ return parsed;
63
+ }
64
+ catch { /* fall through */ }
65
+ /* First-run: no payment method on file. yf chat is gated. */
66
+ const none = { version: 1, plan: 'none' };
67
+ await writeLicense(none);
68
+ return none;
69
+ }
70
+ /** Persist the license cache. */
71
+ export async function writeLicense(cache) {
72
+ const file = licenseFile();
73
+ await fs.mkdir(path.dirname(file), { recursive: true });
74
+ const tmp = file + '.tmp';
75
+ await fs.writeFile(tmp, JSON.stringify(cache, null, 2), 'utf-8');
76
+ await fs.rename(tmp, file);
77
+ }
78
+ /** Helper: build a fresh trial license once the user
79
+ * attaches a payment method (typically called from the
80
+ * Polar webhook handler on `checkout.session.completed`). */
81
+ export function buildTrialLicense(opts) {
82
+ const trialDays = opts.trial_days ?? 7;
83
+ const now = new Date();
84
+ const firstBill = new Date(now.getTime() + trialDays * 24 * 3600 * 1000);
85
+ return {
86
+ version: 1,
87
+ plan: 'trial',
88
+ trial_started_at: now.toISOString(),
89
+ bills_first_on: firstBill.toISOString(),
90
+ cancel_grace_until: firstBill.toISOString(),
91
+ subscription_id: opts.subscription_id,
92
+ payment_method_attached: true,
93
+ ...(opts.user_handle ? { user_handle: opts.user_handle } : {}),
94
+ };
95
+ }
96
+ /** Is the license currently active? Active = the user can
97
+ * use Forge right now. Includes trial (in grace), paid,
98
+ * cancelled-but-still-in-billing-period, institutional,
99
+ * gifted, and free-public. Excludes 'none' (no TC) and
100
+ * expired plans. */
101
+ export function isActive(cache) {
102
+ if (cache.plan === 'none')
103
+ return false;
104
+ if (cache.plan === 'institutional' || cache.plan === 'gifted')
105
+ return true;
106
+ if (cache.plan === 'free-public')
107
+ return true;
108
+ if (cache.plan === 'trial') {
109
+ if (!cache.cancel_grace_until)
110
+ return false;
111
+ return new Date(cache.cancel_grace_until).getTime() > Date.now();
112
+ }
113
+ if (cache.plan === 'paid' || cache.plan === 'cancelled') {
114
+ if (!cache.expires_at)
115
+ return cache.plan === 'paid';
116
+ return new Date(cache.expires_at).getTime() > Date.now();
117
+ }
118
+ return false;
119
+ }
120
+ /** Days until the user gets charged (trial) or expires
121
+ * (paid/cancelled). Null when the plan has no time clock. */
122
+ export function daysUntilBillingOrExpiry(cache) {
123
+ const target = cache.plan === 'trial'
124
+ ? cache.cancel_grace_until
125
+ : cache.expires_at;
126
+ if (!target)
127
+ return null;
128
+ const ms = new Date(target).getTime() - Date.now();
129
+ return Math.floor(ms / (24 * 3600 * 1000));
130
+ }
131
+ /** Back-compat alias kept so existing callers don't break. */
132
+ export function daysRemaining(cache) {
133
+ return daysUntilBillingOrExpiry(cache);
134
+ }
135
+ /** Surface for `yf doctor` and the chat panel. ASCII-only.
136
+ * Internal-only plans (institutional, gifted) DO NOT
137
+ * surface the underlying reason -- they show a generic
138
+ * "free seat" message. Pablo's policy: no advertising
139
+ * discapacidad as a tier. */
140
+ export function describeLicense(cache) {
141
+ if (cache.plan === 'none') {
142
+ return 'no payment method on file. Run `yf license activate` to add a card + start the 7-day free window.';
143
+ }
144
+ if (cache.plan === 'institutional' || cache.plan === 'gifted') {
145
+ return 'free seat -- thanks for being part of Yujin.';
146
+ }
147
+ if (cache.plan === 'free-public')
148
+ return 'free (public repos only)';
149
+ const days = daysUntilBillingOrExpiry(cache);
150
+ if (cache.plan === 'trial') {
151
+ if (days === null)
152
+ return 'trial -- missing dates';
153
+ if (days < 0) {
154
+ return 'trial -- charged on day 8. Run `yf license status` for the next bill date.';
155
+ }
156
+ if (days === 0)
157
+ return 'trial -- last day to cancel sin cargo. Bills tomorrow.';
158
+ return 'trial -- ' + days + ' days left to cancel sin cargo. Day ' + (8 - days) + ' bills USD 10.';
159
+ }
160
+ if (cache.plan === 'paid') {
161
+ if (days === null)
162
+ return 'paid -- next bill date unknown';
163
+ if (days < 0)
164
+ return 'paid -- expired ' + Math.abs(days) + ' days ago';
165
+ return 'paid -- next bill in ' + days + ' day(s)';
166
+ }
167
+ if (cache.plan === 'cancelled') {
168
+ if (days === null || days < 0) {
169
+ return 'cancelled. Run `yf license resubscribe` to reactivate.';
170
+ }
171
+ return 'cancelled -- access ends in ' + days + ' day(s). Run `yf license resubscribe` to keep it.';
172
+ }
173
+ return 'unknown plan';
174
+ }
175
+ //# sourceMappingURL=polar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polar.js","sourceRoot":"","sources":["../../src/core/polar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AA6CzB,MAAM,YAAY,GAAG,cAAc,CAAC;AAEpC;;;;;;;;2DAQ2D;AAC3D,MAAM,CAAC,MAAM,mBAAmB,GAC9B,OAAO,CAAC,GAAG,CAAC,qBAAqB;IACjC,2EAA2E,CAAC;AAE9E;;wBAEwB;AACxB,MAAM,UAAU,gBAAgB,CAAC,UAAmB;IAClD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACzC,IAAI,UAAU;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,sBAAsB,EAAE,UAAU,CAAC,CAAC;IACzE,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,2CAA2C;AAC3C,SAAS,WAAW;IAClB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC;AAC/D,CAAC;AAED;;;kCAGkC;AAClC,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI;YAAE,OAAO,MAAsB,CAAC;IACnF,CAAC;IAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAC9B,6DAA6D;IAC7D,MAAM,IAAI,GAAiB,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACxD,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iCAAiC;AACjC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAmB;IACpD,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC;IAC1B,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACjE,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED;;8DAE8D;AAC9D,MAAM,UAAU,iBAAiB,CAAC,IAIjC;IACC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,SAAS,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;IACzE,OAAO;QACL,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,OAAO;QACb,gBAAgB,EAAE,GAAG,CAAC,WAAW,EAAE;QACnC,cAAc,EAAE,SAAS,CAAC,WAAW,EAAE;QACvC,kBAAkB,EAAE,SAAS,CAAC,WAAW,EAAE;QAC3C,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,uBAAuB,EAAE,IAAI;QAC7B,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/D,CAAC;AACJ,CAAC;AAED;;;;qBAIqB;AACrB,MAAM,UAAU,QAAQ,CAAC,KAAmB;IAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3E,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,kBAAkB;YAAE,OAAO,KAAK,CAAC;QAC5C,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACnE,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC;QACpD,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;8DAC8D;AAC9D,MAAM,UAAU,wBAAwB,CAAC,KAAmB;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,KAAK,OAAO;QACnC,CAAC,CAAC,KAAK,CAAC,kBAAkB;QAC1B,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC;IACrB,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACnD,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,aAAa,CAAC,KAAmB;IAC/C,OAAO,wBAAwB,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED;;;;8BAI8B;AAC9B,MAAM,UAAU,eAAe,CAAC,KAAmB;IACjD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,OAAO,mGAAmG,CAAC;IAC7G,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9D,OAAO,8CAA8C,CAAC;IACxD,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa;QAAE,OAAO,0BAA0B,CAAC;IACpE,MAAM,IAAI,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,wBAAwB,CAAC;QACnD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,OAAO,4EAA4E,CAAC;QACtF,CAAC;QACD,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,wDAAwD,CAAC;QAChF,OAAO,WAAW,GAAG,IAAI,GAAG,sCAAsC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,gBAAgB,CAAC;IACrG,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,gCAAgC,CAAC;QAC3D,IAAI,IAAI,GAAG,CAAC;YAAE,OAAO,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC;QACvE,OAAO,uBAAuB,GAAG,IAAI,GAAG,SAAS,CAAC;IACpD,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC/B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,wDAAwD,CAAC;QAClE,CAAC;QACD,OAAO,8BAA8B,GAAG,IAAI,GAAG,mDAAmD,CAAC;IACrG,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Project picker -- merges three sources into the unified set
3
+ * a mobile / sibling-laptop UI shows when the user wants to
4
+ * choose a project to work on:
5
+ *
6
+ * 1. Local registry projects (this device's known projects).
7
+ * 2. yujin-state remote projects (pulled via projects_sync).
8
+ * 3. mDNS-discovered laptops on the LAN (active sessions).
9
+ *
10
+ * The merge produces a ranked + deduplicated list of
11
+ * `PickerEntry` objects with:
12
+ * - slug (canonical id)
13
+ * - source: 'local' | 'remote_only' | 'lan_session'
14
+ * - localPath? (when this device has a checkout)
15
+ * - lanHost? / lanPort? (when mDNS saw it announcing)
16
+ * - lastActiveAt? (for sort order)
17
+ *
18
+ * The shell uses the source to drive iconography + UX:
19
+ * local -> "open"
20
+ * lan_session -> "join session" (laptop is running yf chat)
21
+ * remote_only -> "clone" or "request access"
22
+ *
23
+ * Pure -- merges inputs, does not perform I/O.
24
+ *
25
+ * ASCII-only.
26
+ */
27
+ import type { ProjectEntry } from './projects.js';
28
+ import type { Discovery as LanDiscovery } from '../lan/mdns_service.js';
29
+ export type PickerSource = 'local' | 'lan_session' | 'remote_only';
30
+ export interface PickerEntry {
31
+ slug: string;
32
+ source: PickerSource;
33
+ /** Sort timestamp (ISO 8601). `lan_session` always wins
34
+ * the tie against `local` (live > cached). */
35
+ lastActiveAt?: string;
36
+ /** Absolute path on this device, when applicable. */
37
+ localPath?: string;
38
+ /** mDNS-resolved host/port for live sessions. */
39
+ lanHost?: string;
40
+ lanPort?: number;
41
+ /** Repo URL (when known). */
42
+ githubRepo?: string;
43
+ /** Build / slug metadata from the mDNS TXT record. */
44
+ lanTxt?: Record<string, string>;
45
+ }
46
+ export interface PickerInputs {
47
+ localProjects: ProjectEntry[];
48
+ lanDiscoveries?: LanDiscovery[];
49
+ }
50
+ export declare function pickProjects(inputs: PickerInputs): PickerEntry[];
51
+ /** Convenience: given the merged list + the explicit active
52
+ * slug, return the entry that should be selected by default
53
+ * on first render. If no active slug, return the top-ranked
54
+ * entry; if empty, null. */
55
+ export declare function defaultActive(entries: PickerEntry[], activeSlug: string | null): PickerEntry | null;
56
+ //# sourceMappingURL=project_picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project_picker.d.ts","sourceRoot":"","sources":["../../src/core/project_picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAExE,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,aAAa,GAAG,aAAa,CAAC;AAEnE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB;mDAC+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;CACjC;AAsBD,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,EAAE,CAiDhE;AAED;;;6BAG6B;AAC7B,wBAAgB,aAAa,CAC3B,OAAO,EAAE,WAAW,EAAE,EACtB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,WAAW,GAAG,IAAI,CAOpB"}
@@ -0,0 +1,86 @@
1
+ /** Rank function: higher = appears first.
2
+ *
3
+ * Layered encoding -- source weight is the dominant term
4
+ * (multiplied by 1e16 so it always outranks any timestamp),
5
+ * timestamp tiebreaks WITHIN each source tier.
6
+ *
7
+ * lan_session always beats local + remote_only.
8
+ * local always beats remote_only.
9
+ * Within a tier, recent lastActiveAt wins. */
10
+ function rankOf(e) {
11
+ const sourceWeight = e.source === 'lan_session' ? 3 :
12
+ e.source === 'local' ? 2 :
13
+ /* remote_only */ 1;
14
+ const ts = e.lastActiveAt ? Date.parse(e.lastActiveAt) : 0;
15
+ /* 1e16 > Number.MAX_SAFE_INTEGER would lose precision; use
16
+ * 1e15 which is ~ 31000 years past epoch in ms -- safe. */
17
+ return sourceWeight * 1e15 + ts;
18
+ }
19
+ export function pickProjects(inputs) {
20
+ const bySlug = new Map();
21
+ /* Step 1: seed from local registry. */
22
+ for (const p of inputs.localProjects) {
23
+ const entry = {
24
+ slug: p.slug,
25
+ source: p.remote_only ? 'remote_only' : 'local',
26
+ };
27
+ if (!p.remote_only)
28
+ entry.localPath = p.path;
29
+ if (p.last_active_at)
30
+ entry.lastActiveAt = p.last_active_at;
31
+ if (p.github_repo)
32
+ entry.githubRepo = p.github_repo;
33
+ bySlug.set(p.slug, entry);
34
+ }
35
+ /* Step 2: overlay LAN discoveries -- they outrank everything
36
+ * while they are live. The instance label IS the slug. */
37
+ for (const d of inputs.lanDiscoveries ?? []) {
38
+ const slug = d.txt.slug ?? d.instance;
39
+ const existing = bySlug.get(slug);
40
+ if (existing) {
41
+ existing.source = 'lan_session';
42
+ existing.lanHost = d.host;
43
+ existing.lanPort = d.port;
44
+ existing.lanTxt = d.txt;
45
+ /* If LAN provides a timestamp via TXT, prefer it. */
46
+ if (d.txt.last_active_at)
47
+ existing.lastActiveAt = d.txt.last_active_at;
48
+ }
49
+ else {
50
+ bySlug.set(slug, {
51
+ slug,
52
+ source: 'lan_session',
53
+ lanHost: d.host,
54
+ lanPort: d.port,
55
+ lanTxt: d.txt,
56
+ lastActiveAt: d.txt.last_active_at,
57
+ });
58
+ }
59
+ }
60
+ /* Step 3: sort by rank descending. Tie-break by slug asc for
61
+ * deterministic output (helps testing + the UI). */
62
+ const out = [...bySlug.values()];
63
+ out.sort((a, b) => {
64
+ const ra = rankOf(a);
65
+ const rb = rankOf(b);
66
+ if (rb !== ra)
67
+ return rb - ra;
68
+ return a.slug.localeCompare(b.slug);
69
+ });
70
+ return out;
71
+ }
72
+ /** Convenience: given the merged list + the explicit active
73
+ * slug, return the entry that should be selected by default
74
+ * on first render. If no active slug, return the top-ranked
75
+ * entry; if empty, null. */
76
+ export function defaultActive(entries, activeSlug) {
77
+ if (entries.length === 0)
78
+ return null;
79
+ if (activeSlug) {
80
+ const hit = entries.find((e) => e.slug === activeSlug);
81
+ if (hit)
82
+ return hit;
83
+ }
84
+ return entries[0] ?? null;
85
+ }
86
+ //# sourceMappingURL=project_picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project_picker.js","sourceRoot":"","sources":["../../src/core/project_picker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ export interface ProjectEntry {
2
+ /** Stable identifier. Derived from yujin.forge.json or
3
+ * from the repo name when none is present. */
4
+ slug: string;
5
+ /** Absolute path on this device (Windows or POSIX). */
6
+ path: string;
7
+ /** Remote repo URL when known (https://github.com/org/repo). */
8
+ github_repo?: string;
9
+ /** Last time this device opened this project, ISO 8601. */
10
+ last_active_at?: string;
11
+ /** Cached target + mode from yujin.forge.json. */
12
+ target?: string;
13
+ mode?: string;
14
+ /** True if this device cached the entry from GitHub-anchored
15
+ * state (i.e. the user knows the slug but the path on
16
+ * this device is `null`). */
17
+ remote_only?: boolean;
18
+ /** Last-modified ISO 8601 timestamp -- updated on every
19
+ * upsertProject. Used for last-writer-wins merge in
20
+ * projects_sync.ts. */
21
+ updated_at?: string;
22
+ }
23
+ export interface ProjectsRegistry {
24
+ version: 1;
25
+ /** Slug of the currently-active project on this device. */
26
+ active_slug: string | null;
27
+ projects: ProjectEntry[];
28
+ }
29
+ /** Resolve the path to the per-user registry directory. */
30
+ export declare function registryDir(): string;
31
+ /** Read the local registry. Returns empty default when the
32
+ * file does not exist. */
33
+ export declare function readRegistry(): Promise<ProjectsRegistry>;
34
+ /** Persist the registry atomically. */
35
+ export declare function writeRegistry(reg: ProjectsRegistry): Promise<void>;
36
+ /** Add or update a project entry. Identified by slug; if the
37
+ * slug exists, merges fields (last_active_at + cached fields
38
+ * win on update; path stays unless explicitly replaced). */
39
+ export declare function upsertProject(entry: ProjectEntry): Promise<ProjectsRegistry>;
40
+ /** Remove a project from the local registry. Does NOT delete
41
+ * the repo on disk. */
42
+ export declare function removeProject(slug: string): Promise<ProjectsRegistry>;
43
+ /** Mark a project as active. Returns the new registry +
44
+ * updates last_active_at on the targeted entry. */
45
+ export declare function setActive(slug: string): Promise<ProjectsRegistry>;
46
+ /** Get the slug of the currently-active project (or null). */
47
+ export declare function activeSlug(): Promise<string | null>;
48
+ /** Look up a project entry by slug. */
49
+ export declare function getProject(slug: string): Promise<ProjectEntry | undefined>;
50
+ /** Read the project's yujin.forge.json + derive a slug from
51
+ * it (project.slug field, or fall back to the directory
52
+ * basename). */
53
+ export declare function deriveSlugFromPath(projectPath: string): Promise<string>;
54
+ /** Scan a directory tree for `yujin.forge.json` files +
55
+ * return their parent dirs. Skips node_modules, .git, dist.
56
+ * Depth-limited to avoid pathological FS layouts. */
57
+ export declare function scanForProjects(scanRoot: string, maxDepth?: number): Promise<string[]>;
58
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/core/projects.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,YAAY;IAC3B;mDAC+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;kCAE8B;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;4BAEwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX,2DAA2D;IAC3D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAID,2DAA2D;AAC3D,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;2BAC2B;AAC3B,wBAAsB,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAU9D;AAED,uCAAuC;AACvC,wBAAsB,aAAa,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxE;AAED;;6DAE6D;AAC7D,wBAAsB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAWlF;AAED;wBACwB;AACxB,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAM3E;AAED;oDACoD;AACpD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAQvE;AAED,8DAA8D;AAC9D,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGzD;AAED,uCAAuC;AACvC,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAGhF;AAED;;iBAEiB;AACjB,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU7E;AAED;;sDAEsD;AACtD,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAU,GACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CAsBnB"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Project registry (PLAN #10, GitHub-anchored).
3
+ *
4
+ * Each device keeps a local cache of known Yujin projects:
5
+ * ~/.yujin-forge/projects.json
6
+ *
7
+ * The truth-bus is GitHub: the user's `yujin-state` repo
8
+ * stores authoritative metadata that other devices can pull.
9
+ * This module exposes only the LOCAL store; GitHub sync
10
+ * lives in `projects_sync.ts`.
11
+ *
12
+ * ASCII-only.
13
+ */
14
+ import { promises as fs } from 'node:fs';
15
+ import path from 'node:path';
16
+ import os from 'node:os';
17
+ const REGISTRY_FILE = 'projects.json';
18
+ /** Resolve the path to the per-user registry directory. */
19
+ export function registryDir() {
20
+ return path.join(os.homedir(), '.yujin-forge');
21
+ }
22
+ /** Read the local registry. Returns empty default when the
23
+ * file does not exist. */
24
+ export async function readRegistry() {
25
+ const file = path.join(registryDir(), REGISTRY_FILE);
26
+ try {
27
+ const raw = await fs.readFile(file, 'utf-8');
28
+ const parsed = JSON.parse(raw);
29
+ if (parsed && parsed.version === 1 && Array.isArray(parsed.projects)) {
30
+ return parsed;
31
+ }
32
+ }
33
+ catch { /* fall through */ }
34
+ return { version: 1, active_slug: null, projects: [] };
35
+ }
36
+ /** Persist the registry atomically. */
37
+ export async function writeRegistry(reg) {
38
+ const dir = registryDir();
39
+ await fs.mkdir(dir, { recursive: true });
40
+ const file = path.join(dir, REGISTRY_FILE);
41
+ const tmp = file + '.tmp';
42
+ await fs.writeFile(tmp, JSON.stringify(reg, null, 2), 'utf-8');
43
+ await fs.rename(tmp, file);
44
+ }
45
+ /** Add or update a project entry. Identified by slug; if the
46
+ * slug exists, merges fields (last_active_at + cached fields
47
+ * win on update; path stays unless explicitly replaced). */
48
+ export async function upsertProject(entry) {
49
+ const reg = await readRegistry();
50
+ const idx = reg.projects.findIndex((p) => p.slug === entry.slug);
51
+ const stamped = { ...entry, updated_at: entry.updated_at ?? new Date().toISOString() };
52
+ if (idx >= 0) {
53
+ reg.projects[idx] = { ...reg.projects[idx], ...stamped };
54
+ }
55
+ else {
56
+ reg.projects.push(stamped);
57
+ }
58
+ await writeRegistry(reg);
59
+ return reg;
60
+ }
61
+ /** Remove a project from the local registry. Does NOT delete
62
+ * the repo on disk. */
63
+ export async function removeProject(slug) {
64
+ const reg = await readRegistry();
65
+ reg.projects = reg.projects.filter((p) => p.slug !== slug);
66
+ if (reg.active_slug === slug)
67
+ reg.active_slug = null;
68
+ await writeRegistry(reg);
69
+ return reg;
70
+ }
71
+ /** Mark a project as active. Returns the new registry +
72
+ * updates last_active_at on the targeted entry. */
73
+ export async function setActive(slug) {
74
+ const reg = await readRegistry();
75
+ const entry = reg.projects.find((p) => p.slug === slug);
76
+ if (!entry)
77
+ throw new Error('project not in registry: ' + slug);
78
+ entry.last_active_at = new Date().toISOString();
79
+ reg.active_slug = slug;
80
+ await writeRegistry(reg);
81
+ return reg;
82
+ }
83
+ /** Get the slug of the currently-active project (or null). */
84
+ export async function activeSlug() {
85
+ const reg = await readRegistry();
86
+ return reg.active_slug;
87
+ }
88
+ /** Look up a project entry by slug. */
89
+ export async function getProject(slug) {
90
+ const reg = await readRegistry();
91
+ return reg.projects.find((p) => p.slug === slug);
92
+ }
93
+ /** Read the project's yujin.forge.json + derive a slug from
94
+ * it (project.slug field, or fall back to the directory
95
+ * basename). */
96
+ export async function deriveSlugFromPath(projectPath) {
97
+ try {
98
+ const raw = await fs.readFile(path.join(projectPath, 'yujin.forge.json'), 'utf-8');
99
+ const parsed = JSON.parse(raw);
100
+ if (typeof parsed.slug === 'string' && parsed.slug.length > 0)
101
+ return parsed.slug;
102
+ if (typeof parsed.project_name === 'string') {
103
+ return slugify(parsed.project_name);
104
+ }
105
+ }
106
+ catch { /* not yet a Yujin project */ }
107
+ return slugify(path.basename(projectPath));
108
+ }
109
+ /** Scan a directory tree for `yujin.forge.json` files +
110
+ * return their parent dirs. Skips node_modules, .git, dist.
111
+ * Depth-limited to avoid pathological FS layouts. */
112
+ export async function scanForProjects(scanRoot, maxDepth = 3) {
113
+ const out = [];
114
+ await walk(scanRoot, 0);
115
+ return out;
116
+ async function walk(dir, depth) {
117
+ if (depth > maxDepth)
118
+ return;
119
+ let entries;
120
+ try {
121
+ entries = await fs.readdir(dir, { withFileTypes: true });
122
+ }
123
+ catch {
124
+ return;
125
+ }
126
+ for (const e of entries) {
127
+ if (e.name === 'node_modules' || e.name === '.git' || e.name === 'dist'
128
+ || e.name === '.yujin-forge' || e.name.startsWith('.'))
129
+ continue;
130
+ const abs = path.join(dir, e.name);
131
+ if (e.isFile() && e.name === 'yujin.forge.json') {
132
+ out.push(dir);
133
+ }
134
+ else if (e.isDirectory()) {
135
+ await walk(abs, depth + 1);
136
+ }
137
+ }
138
+ }
139
+ }
140
+ function slugify(s) {
141
+ return s.toLowerCase()
142
+ .replace(/[^a-z0-9]+/g, '-')
143
+ .replace(/^-+|-+$/g, '')
144
+ .slice(0, 64) || 'unnamed';
145
+ }
146
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/core/projects.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * GitHub-anchored project sync (PLAN #10, soft #5).
3
+ *
4
+ * The user owns a private GitHub repo `yujin-state` whose
5
+ * `projects.json` file is the cross-device truth bus for their
6
+ * Forge project registry. This module:
7
+ *
8
+ * - Bootstraps the repo + file on first sync (creates the
9
+ * repo + commits an empty projects.json).
10
+ * - Pulls the remote file via the GitHub Contents API.
11
+ * - Merges per-slug into the local registry using
12
+ * last-writer-wins on `updated_at`.
13
+ * - Pushes the merged result back.
14
+ *
15
+ * Per-device fields (path, mode-on-this-device) are STRIPPED
16
+ * before push -- only the cross-device fields (slug,
17
+ * github_repo, target, mode-default, last_active_at,
18
+ * updated_at) ride.
19
+ *
20
+ * Auth: vault slot `github_token` with `repo` scope. Same slot
21
+ * used by `yf repo init` + the chat tools.
22
+ *
23
+ * Loud failures: missing vault slot, non-2xx API, optimistic
24
+ * concurrency conflict (412) -> retry once after re-pulling.
25
+ *
26
+ * ASCII-only.
27
+ */
28
+ import { type ProjectEntry, type ProjectsRegistry } from './projects.js';
29
+ declare const STATE_FILE_SCHEMA = "yujin-state/projects.json/v1";
30
+ export interface SyncRemoteEntry {
31
+ slug: string;
32
+ github_repo?: string;
33
+ target?: string;
34
+ mode?: string;
35
+ last_active_at?: string;
36
+ updated_at: string;
37
+ }
38
+ export interface SyncRemoteState {
39
+ version: 1;
40
+ schema: typeof STATE_FILE_SCHEMA;
41
+ projects: SyncRemoteEntry[];
42
+ }
43
+ export interface SyncDeps {
44
+ fetchImpl?: typeof fetch;
45
+ /** Returns the PAT from the vault. */
46
+ tokenProvider: () => Promise<string>;
47
+ /** Returns the GitHub username (used to address the state repo). */
48
+ ownerProvider?: () => Promise<string>;
49
+ /** Override the state repo name (for tests). */
50
+ stateRepoName?: string;
51
+ }
52
+ export interface SyncResult {
53
+ ok: boolean;
54
+ remote_repo: string;
55
+ /** Slugs created on the local side as a result of the pull. */
56
+ pulled_new: string[];
57
+ /** Slugs updated on the local side (remote was newer). */
58
+ pulled_updated: string[];
59
+ /** Slugs pushed (local was newer or remote did not have them). */
60
+ pushed: string[];
61
+ /** True when the state repo had to be created on this run. */
62
+ bootstrapped: boolean;
63
+ }
64
+ export declare function toRemoteEntry(p: ProjectEntry): SyncRemoteEntry;
65
+ /** Per-slug merge: keep the entry with the higher updated_at.
66
+ * Returns [merged, classifications] -- classifications used
67
+ * by SyncResult. */
68
+ export declare function mergeRegistries(local: ProjectsRegistry, remote: SyncRemoteState): {
69
+ merged: ProjectsRegistry;
70
+ pulled_new: string[];
71
+ pulled_updated: string[];
72
+ pushed: string[];
73
+ };
74
+ /** Run a full bidirectional sync. Caller orchestrates retries. */
75
+ export declare function syncProjects(opts: {
76
+ pullOnly?: boolean;
77
+ pushOnly?: boolean;
78
+ } | undefined, deps: SyncDeps): Promise<SyncResult>;
79
+ export {};
80
+ //# sourceMappingURL=projects_sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects_sync.d.ts","sourceRoot":"","sources":["../../src/core/projects_sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EACwB,KAAK,YAAY,EAAE,KAAK,gBAAgB,EACtE,MAAM,eAAe,CAAC;AAKvB,QAAA,MAAM,iBAAiB,iCAAiC,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,OAAO,iBAAiB,CAAC;IACjC,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,sCAAsC;IACtC,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0DAA0D;IAC1D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8DAA8D;IAC9D,YAAY,EAAE,OAAO,CAAC;CACvB;AAGD,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,GAAG,eAAe,CAS9D;AAED;;qBAEqB;AACrB,wBAAgB,eAAe,CAC7B,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,eAAe,GACtB;IACD,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CA8DA;AAsHD,kEAAkE;AAClE,wBAAsB,YAAY,CAChC,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,YAAK,EACrD,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,UAAU,CAAC,CA4DrB"}