@logictan/dsh-config-manager 0.1.60

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 (429) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/cordis.patch.yml +21 -0
  4. package/lib/adapters/agent-instructions.d.ts +13 -0
  5. package/lib/adapters/agent-instructions.js +46 -0
  6. package/lib/adapters/agent-presets.d.ts +13 -0
  7. package/lib/adapters/agent-presets.js +14 -0
  8. package/lib/adapters/credentials.d.ts +37 -0
  9. package/lib/adapters/credentials.js +122 -0
  10. package/lib/adapters/file-collection.d.ts +15 -0
  11. package/lib/adapters/file-collection.js +127 -0
  12. package/lib/adapters/index.d.ts +57 -0
  13. package/lib/adapters/index.js +55 -0
  14. package/lib/adapters/link-report.d.ts +16 -0
  15. package/lib/adapters/link-report.js +47 -0
  16. package/lib/adapters/mcp.d.ts +28 -0
  17. package/lib/adapters/mcp.js +178 -0
  18. package/lib/adapters/plugin-files.d.ts +18 -0
  19. package/lib/adapters/plugin-files.js +142 -0
  20. package/lib/adapters/plugins.d.ts +41 -0
  21. package/lib/adapters/plugins.js +603 -0
  22. package/lib/adapters/pnpm-workspace.d.ts +26 -0
  23. package/lib/adapters/pnpm-workspace.js +147 -0
  24. package/lib/adapters/prompts.d.ts +30 -0
  25. package/lib/adapters/prompts.js +231 -0
  26. package/lib/adapters/providers.d.ts +34 -0
  27. package/lib/adapters/providers.js +155 -0
  28. package/lib/adapters/self.d.ts +17 -0
  29. package/lib/adapters/self.js +71 -0
  30. package/lib/adapters/sessions.d.ts +14 -0
  31. package/lib/adapters/sessions.js +15 -0
  32. package/lib/adapters/settings.d.ts +28 -0
  33. package/lib/adapters/settings.js +165 -0
  34. package/lib/adapters/skills.d.ts +13 -0
  35. package/lib/adapters/skills.js +14 -0
  36. package/lib/adapters/test-helpers.d.ts +125 -0
  37. package/lib/adapters/test-helpers.js +236 -0
  38. package/lib/adapters/ui.d.ts +27 -0
  39. package/lib/adapters/ui.js +89 -0
  40. package/lib/adapters/workspaces.d.ts +13 -0
  41. package/lib/adapters/workspaces.js +106 -0
  42. package/lib/client.d.ts +1026 -0
  43. package/lib/client.js +10047 -0
  44. package/lib/core/analyzer.d.ts +99 -0
  45. package/lib/core/analyzer.js +934 -0
  46. package/lib/core/backup.d.ts +138 -0
  47. package/lib/core/backup.js +513 -0
  48. package/lib/core/boot-rescue.d.ts +146 -0
  49. package/lib/core/boot-rescue.js +618 -0
  50. package/lib/core/cache-cleaner.d.ts +31 -0
  51. package/lib/core/cache-cleaner.js +95 -0
  52. package/lib/core/config-lifecycle.d.ts +180 -0
  53. package/lib/core/config-lifecycle.js +481 -0
  54. package/lib/core/config-snapshot.d.ts +135 -0
  55. package/lib/core/config-snapshot.js +424 -0
  56. package/lib/core/config-state.d.ts +70 -0
  57. package/lib/core/config-state.js +177 -0
  58. package/lib/core/consult-source.d.ts +58 -0
  59. package/lib/core/consult-source.js +260 -0
  60. package/lib/core/crash-report.d.ts +84 -0
  61. package/lib/core/crash-report.js +317 -0
  62. package/lib/core/exporter.d.ts +68 -0
  63. package/lib/core/exporter.js +375 -0
  64. package/lib/core/ghost-sweep.d.ts +19 -0
  65. package/lib/core/ghost-sweep.js +52 -0
  66. package/lib/core/importer.d.ts +60 -0
  67. package/lib/core/importer.js +29 -0
  68. package/lib/core/index.d.ts +14 -0
  69. package/lib/core/index.js +14 -0
  70. package/lib/core/journal.d.ts +197 -0
  71. package/lib/core/journal.js +418 -0
  72. package/lib/core/local-plugin-host.d.ts +35 -0
  73. package/lib/core/local-plugin-host.js +120 -0
  74. package/lib/core/local-plugin-pack.d.ts +135 -0
  75. package/lib/core/local-plugin-pack.js +278 -0
  76. package/lib/core/messages.d.ts +310 -0
  77. package/lib/core/messages.js +621 -0
  78. package/lib/core/migration-consult.d.ts +117 -0
  79. package/lib/core/migration-consult.js +363 -0
  80. package/lib/core/migration-history.d.ts +153 -0
  81. package/lib/core/migration-history.js +411 -0
  82. package/lib/core/model-tools.d.ts +50 -0
  83. package/lib/core/model-tools.js +148 -0
  84. package/lib/core/msg-types.d.ts +8 -0
  85. package/lib/core/msg-types.js +6 -0
  86. package/lib/core/phase3-child-crash.d.ts +1 -0
  87. package/lib/core/phase3-child-crash.js +87 -0
  88. package/lib/core/phase3-host.d.ts +138 -0
  89. package/lib/core/phase3-host.js +359 -0
  90. package/lib/core/phase3-prod-child.d.ts +1 -0
  91. package/lib/core/phase3-prod-child.js +35 -0
  92. package/lib/core/phase4-crash-child.d.ts +1 -0
  93. package/lib/core/phase4-crash-child.js +103 -0
  94. package/lib/core/plugin-cli.d.ts +150 -0
  95. package/lib/core/plugin-cli.js +497 -0
  96. package/lib/core/reconcile.d.ts +120 -0
  97. package/lib/core/reconcile.js +389 -0
  98. package/lib/core/recovery-orchestrator.d.ts +94 -0
  99. package/lib/core/recovery-orchestrator.js +327 -0
  100. package/lib/core/restore.d.ts +162 -0
  101. package/lib/core/restore.js +703 -0
  102. package/lib/core/rollback.d.ts +12 -0
  103. package/lib/core/rollback.js +145 -0
  104. package/lib/core/run-registry.d.ts +85 -0
  105. package/lib/core/run-registry.js +157 -0
  106. package/lib/core/startup-barrier.d.ts +53 -0
  107. package/lib/core/startup-barrier.js +77 -0
  108. package/lib/core/types.d.ts +515 -0
  109. package/lib/core/types.js +27 -0
  110. package/lib/core/undo.d.ts +73 -0
  111. package/lib/core/undo.js +87 -0
  112. package/lib/core/validator.d.ts +18 -0
  113. package/lib/core/validator.js +77 -0
  114. package/lib/core/verify-recovery.d.ts +21 -0
  115. package/lib/core/verify-recovery.js +256 -0
  116. package/lib/core/watcher.d.ts +101 -0
  117. package/lib/core/watcher.js +219 -0
  118. package/lib/index.d.ts +238 -0
  119. package/lib/index.js +2764 -0
  120. package/lib/migrations/index.d.ts +29 -0
  121. package/lib/migrations/index.js +56 -0
  122. package/lib/migrations/v1-to-v2.d.ts +9 -0
  123. package/lib/migrations/v1-to-v2.js +10 -0
  124. package/lib/profiles/index.d.ts +4 -0
  125. package/lib/profiles/index.js +5 -0
  126. package/lib/profiles/profile-manager.d.ts +106 -0
  127. package/lib/profiles/profile-manager.js +543 -0
  128. package/lib/schema/config.d.ts +28 -0
  129. package/lib/schema/config.js +165 -0
  130. package/lib/schema/index.d.ts +24 -0
  131. package/lib/schema/index.js +26 -0
  132. package/lib/schema/manifest.d.ts +28 -0
  133. package/lib/schema/manifest.js +109 -0
  134. package/lib/schema/tombstones.d.ts +46 -0
  135. package/lib/schema/tombstones.js +64 -0
  136. package/lib/schema/types.d.ts +232 -0
  137. package/lib/schema/types.js +8 -0
  138. package/lib/schema/versions.d.ts +27 -0
  139. package/lib/schema/versions.js +50 -0
  140. package/lib/security/encryption.d.ts +85 -0
  141. package/lib/security/encryption.js +279 -0
  142. package/lib/security/index.d.ts +16 -0
  143. package/lib/security/index.js +17 -0
  144. package/lib/security/integrity.d.ts +30 -0
  145. package/lib/security/integrity.js +78 -0
  146. package/lib/security/redaction.d.ts +26 -0
  147. package/lib/security/redaction.js +95 -0
  148. package/lib/security/secret-scanner.d.ts +86 -0
  149. package/lib/security/secret-scanner.js +404 -0
  150. package/lib/security/vault.d.ts +59 -0
  151. package/lib/security/vault.js +151 -0
  152. package/lib/security/zip-security.d.ts +27 -0
  153. package/lib/security/zip-security.js +154 -0
  154. package/lib/sync/autosync-config.d.ts +39 -0
  155. package/lib/sync/autosync-config.js +151 -0
  156. package/lib/sync/autosync-scheduler.d.ts +139 -0
  157. package/lib/sync/autosync-scheduler.js +564 -0
  158. package/lib/sync/fs.d.ts +18 -0
  159. package/lib/sync/fs.js +49 -0
  160. package/lib/sync/git/git-transport.d.ts +100 -0
  161. package/lib/sync/git/git-transport.js +437 -0
  162. package/lib/sync/github-auth.d.ts +131 -0
  163. package/lib/sync/github-auth.js +264 -0
  164. package/lib/sync/layout.d.ts +43 -0
  165. package/lib/sync/layout.js +203 -0
  166. package/lib/sync/snapshot-json.d.ts +9 -0
  167. package/lib/sync/snapshot-json.js +107 -0
  168. package/lib/sync/sync-config.d.ts +81 -0
  169. package/lib/sync/sync-config.js +281 -0
  170. package/lib/sync/sync-engine.d.ts +269 -0
  171. package/lib/sync/sync-engine.js +591 -0
  172. package/lib/sync/sync-history.d.ts +42 -0
  173. package/lib/sync/sync-history.js +78 -0
  174. package/lib/sync/sync-selection.d.ts +29 -0
  175. package/lib/sync/sync-selection.js +127 -0
  176. package/lib/sync/sync-session.d.ts +40 -0
  177. package/lib/sync/sync-session.js +54 -0
  178. package/lib/sync/sync-state.d.ts +37 -0
  179. package/lib/sync/sync-state.js +108 -0
  180. package/lib/sync/transport.d.ts +75 -0
  181. package/lib/sync/transport.js +55 -0
  182. package/lib/sync/ui-prefs.d.ts +34 -0
  183. package/lib/sync/ui-prefs.js +109 -0
  184. package/lib/sync/webdav/webdav-transport.d.ts +77 -0
  185. package/lib/sync/webdav/webdav-transport.js +451 -0
  186. package/lib/ui/errors.d.ts +19 -0
  187. package/lib/ui/errors.js +81 -0
  188. package/lib/ui/export-flow.d.ts +82 -0
  189. package/lib/ui/export-flow.js +98 -0
  190. package/lib/ui/i18n.d.ts +289 -0
  191. package/lib/ui/i18n.js +581 -0
  192. package/lib/ui/migration-consult-view.d.ts +91 -0
  193. package/lib/ui/migration-consult-view.js +115 -0
  194. package/lib/ui/progress.d.ts +38 -0
  195. package/lib/ui/progress.js +87 -0
  196. package/lib/ui/report.d.ts +23 -0
  197. package/lib/ui/report.js +153 -0
  198. package/lib/ui/test-helpers.d.ts +69 -0
  199. package/lib/ui/test-helpers.js +154 -0
  200. package/lib/ui/types.d.ts +257 -0
  201. package/lib/ui/types.js +21 -0
  202. package/lib/utils/atomic-write.d.ts +88 -0
  203. package/lib/utils/atomic-write.js +399 -0
  204. package/lib/utils/env-lock.d.ts +393 -0
  205. package/lib/utils/env-lock.js +930 -0
  206. package/lib/utils/hashing.d.ts +17 -0
  207. package/lib/utils/hashing.js +41 -0
  208. package/lib/utils/json.d.ts +24 -0
  209. package/lib/utils/json.js +76 -0
  210. package/lib/utils/logger.d.ts +39 -0
  211. package/lib/utils/logger.js +82 -0
  212. package/lib/utils/paths.d.ts +42 -0
  213. package/lib/utils/paths.js +189 -0
  214. package/lib/utils/proxy.d.ts +69 -0
  215. package/lib/utils/proxy.js +455 -0
  216. package/lib/utils/recursive-walk.d.ts +24 -0
  217. package/lib/utils/recursive-walk.js +134 -0
  218. package/lib/utils/zip.d.ts +54 -0
  219. package/lib/utils/zip.js +285 -0
  220. package/package.json +188 -0
  221. package/src/adapters/agent-instructions.ts +48 -0
  222. package/src/adapters/agent-presets.ts +14 -0
  223. package/src/adapters/credentials.test.ts +90 -0
  224. package/src/adapters/credentials.ts +143 -0
  225. package/src/adapters/file-collection.ts +137 -0
  226. package/src/adapters/files.test.ts +353 -0
  227. package/src/adapters/index.ts +94 -0
  228. package/src/adapters/link-report.test.ts +65 -0
  229. package/src/adapters/link-report.ts +61 -0
  230. package/src/adapters/mcp.test.ts +96 -0
  231. package/src/adapters/mcp.ts +185 -0
  232. package/src/adapters/plugin-files.ts +146 -0
  233. package/src/adapters/plugins.test.ts +326 -0
  234. package/src/adapters/plugins.ts +615 -0
  235. package/src/adapters/pnpm-workspace.test.ts +87 -0
  236. package/src/adapters/pnpm-workspace.ts +150 -0
  237. package/src/adapters/prompts.test.ts +110 -0
  238. package/src/adapters/prompts.ts +230 -0
  239. package/src/adapters/providers.test.ts +126 -0
  240. package/src/adapters/providers.ts +173 -0
  241. package/src/adapters/roundtrip.test.ts +190 -0
  242. package/src/adapters/self.test.ts +92 -0
  243. package/src/adapters/self.ts +75 -0
  244. package/src/adapters/sessions.ts +15 -0
  245. package/src/adapters/settings.test.ts +100 -0
  246. package/src/adapters/settings.ts +187 -0
  247. package/src/adapters/skills.ts +14 -0
  248. package/src/adapters/test-helpers.ts +248 -0
  249. package/src/adapters/ui.test.ts +57 -0
  250. package/src/adapters/ui.ts +108 -0
  251. package/src/adapters/workspaces.test.ts +106 -0
  252. package/src/adapters/workspaces.ts +109 -0
  253. package/src/client/ConfigManagerSection.tsx +90 -0
  254. package/src/client/api.ts +66 -0
  255. package/src/client/client-types.ts +18 -0
  256. package/src/client/common/ErrorBanner.tsx +73 -0
  257. package/src/client/common/Icon.tsx +129 -0
  258. package/src/client/common/Modal.tsx +173 -0
  259. package/src/client/common/ToastViewport.tsx +73 -0
  260. package/src/client/common/toast-store.test.ts +192 -0
  261. package/src/client/common/toast-store.ts +181 -0
  262. package/src/client/common/toast-types.ts +7 -0
  263. package/src/client/common/ui.tsx +342 -0
  264. package/src/client/config-manager.module.css +2971 -0
  265. package/src/client/css-modules.d.ts +8 -0
  266. package/src/client/index.ts +65 -0
  267. package/src/client/locales.ts +56 -0
  268. package/src/client/lucide-icons.d.ts +38 -0
  269. package/src/client/run-store.test.ts +223 -0
  270. package/src/client/run-store.ts +372 -0
  271. package/src/client/sync/SyncConfirmView.tsx +379 -0
  272. package/src/client/sync/SyncHistoryView.test.ts +40 -0
  273. package/src/client/sync/SyncHistoryView.tsx +220 -0
  274. package/src/client/sync/SyncSettingsView.tsx +1535 -0
  275. package/src/client/sync/history-model.test.ts +194 -0
  276. package/src/client/sync/history-model.ts +195 -0
  277. package/src/client/sync/sync-api.test.ts +585 -0
  278. package/src/client/sync/sync-api.ts +543 -0
  279. package/src/client/sync/sync-locales.ts +416 -0
  280. package/src/client/sync/sync-push-preview.test.ts +52 -0
  281. package/src/client/sync/sync-view-v2.test.ts +213 -0
  282. package/src/client/sync/sync-view.test.ts +487 -0
  283. package/src/client/sync/sync-view.ts +759 -0
  284. package/src/core/analyzer.ts +1068 -0
  285. package/src/core/backup.ts +605 -0
  286. package/src/core/boot-rescue.test.ts +847 -0
  287. package/src/core/boot-rescue.ts +737 -0
  288. package/src/core/cache-cleaner.test.ts +169 -0
  289. package/src/core/cache-cleaner.ts +121 -0
  290. package/src/core/config-lifecycle.test.ts +727 -0
  291. package/src/core/config-lifecycle.ts +573 -0
  292. package/src/core/config-snapshot.test.ts +369 -0
  293. package/src/core/config-snapshot.ts +522 -0
  294. package/src/core/config-state.test.ts +381 -0
  295. package/src/core/config-state.ts +207 -0
  296. package/src/core/consult-source.test.ts +199 -0
  297. package/src/core/consult-source.ts +293 -0
  298. package/src/core/crash-report.test.ts +380 -0
  299. package/src/core/crash-report.ts +369 -0
  300. package/src/core/exporter.ts +417 -0
  301. package/src/core/ghost-sweep.test.ts +94 -0
  302. package/src/core/ghost-sweep.ts +67 -0
  303. package/src/core/importer.ts +83 -0
  304. package/src/core/index.ts +56 -0
  305. package/src/core/journal.test.ts +257 -0
  306. package/src/core/journal.ts +518 -0
  307. package/src/core/local-plugin-host.ts +152 -0
  308. package/src/core/local-plugin-pack.test.ts +359 -0
  309. package/src/core/local-plugin-pack.ts +363 -0
  310. package/src/core/messages.test.ts +54 -0
  311. package/src/core/messages.ts +664 -0
  312. package/src/core/migration-consult.test.ts +315 -0
  313. package/src/core/migration-consult.ts +494 -0
  314. package/src/core/migration-history.test.ts +319 -0
  315. package/src/core/migration-history.ts +484 -0
  316. package/src/core/model-tools.test.ts +190 -0
  317. package/src/core/model-tools.ts +208 -0
  318. package/src/core/msg-types.ts +10 -0
  319. package/src/core/phase3-child-crash.ts +95 -0
  320. package/src/core/phase3-host.ts +411 -0
  321. package/src/core/phase3-p1.test.ts +212 -0
  322. package/src/core/phase3-p2b-fingerprint.test.ts +189 -0
  323. package/src/core/phase3-prod-child.ts +36 -0
  324. package/src/core/phase3-production-integration.test.ts +286 -0
  325. package/src/core/phase4-crash-child.ts +101 -0
  326. package/src/core/phase4-crash-injection.test.ts +166 -0
  327. package/src/core/plugin-cli.fs.test.ts +184 -0
  328. package/src/core/plugin-cli.test.ts +137 -0
  329. package/src/core/plugin-cli.ts +563 -0
  330. package/src/core/reconcile.test.ts +392 -0
  331. package/src/core/reconcile.ts +498 -0
  332. package/src/core/recovery-orchestrator.test.ts +145 -0
  333. package/src/core/recovery-orchestrator.ts +366 -0
  334. package/src/core/restore-manage.test.ts +94 -0
  335. package/src/core/restore.ts +849 -0
  336. package/src/core/rollback.ts +151 -0
  337. package/src/core/run-progress.test.ts +237 -0
  338. package/src/core/run-registry.test.ts +232 -0
  339. package/src/core/run-registry.ts +209 -0
  340. package/src/core/smoke.test.ts +842 -0
  341. package/src/core/startup-barrier.ts +99 -0
  342. package/src/core/types.ts +542 -0
  343. package/src/core/undo.ts +116 -0
  344. package/src/core/validator.ts +84 -0
  345. package/src/core/verify-recovery.ts +265 -0
  346. package/src/core/watcher.test.ts +326 -0
  347. package/src/core/watcher.ts +263 -0
  348. package/src/index.facade.test.ts +265 -0
  349. package/src/index.sync.test.ts +217 -0
  350. package/src/index.ts +2975 -0
  351. package/src/migrations/index.ts +71 -0
  352. package/src/migrations/v1-to-v2.ts +17 -0
  353. package/src/profiles/index.ts +8 -0
  354. package/src/profiles/profile-manager.test.ts +282 -0
  355. package/src/profiles/profile-manager.ts +630 -0
  356. package/src/schema/config.ts +165 -0
  357. package/src/schema/index.ts +71 -0
  358. package/src/schema/manifest-schema.test.ts +519 -0
  359. package/src/schema/manifest.ts +124 -0
  360. package/src/schema/tombstones.test.ts +131 -0
  361. package/src/schema/tombstones.ts +89 -0
  362. package/src/schema/types.ts +245 -0
  363. package/src/schema/versions.ts +55 -0
  364. package/src/security/encryption.ts +335 -0
  365. package/src/security/index.ts +16 -0
  366. package/src/security/integrity.ts +102 -0
  367. package/src/security/redaction.ts +101 -0
  368. package/src/security/secret-scanner.ts +445 -0
  369. package/src/security/security.test.ts +1215 -0
  370. package/src/security/vault.test.ts +164 -0
  371. package/src/security/vault.ts +198 -0
  372. package/src/security/zip-security.ts +180 -0
  373. package/src/sync/autosync-config.test.ts +147 -0
  374. package/src/sync/autosync-config.ts +179 -0
  375. package/src/sync/autosync-scheduler.test.ts +490 -0
  376. package/src/sync/autosync-scheduler.ts +646 -0
  377. package/src/sync/fs.ts +48 -0
  378. package/src/sync/git/git-transport.test.ts +673 -0
  379. package/src/sync/git/git-transport.ts +491 -0
  380. package/src/sync/github-auth.test.ts +280 -0
  381. package/src/sync/github-auth.ts +356 -0
  382. package/src/sync/layout.test.ts +270 -0
  383. package/src/sync/layout.ts +222 -0
  384. package/src/sync/snapshot-json.test.ts +119 -0
  385. package/src/sync/snapshot-json.ts +128 -0
  386. package/src/sync/sync-config.test.ts +310 -0
  387. package/src/sync/sync-config.ts +299 -0
  388. package/src/sync/sync-engine.test.ts +1074 -0
  389. package/src/sync/sync-engine.ts +754 -0
  390. package/src/sync/sync-history.test.ts +116 -0
  391. package/src/sync/sync-history.ts +120 -0
  392. package/src/sync/sync-selection.test.ts +186 -0
  393. package/src/sync/sync-selection.ts +143 -0
  394. package/src/sync/sync-session.test.ts +143 -0
  395. package/src/sync/sync-session.ts +77 -0
  396. package/src/sync/sync-state.test.ts +164 -0
  397. package/src/sync/sync-state.ts +131 -0
  398. package/src/sync/transport.test.ts +111 -0
  399. package/src/sync/transport.ts +118 -0
  400. package/src/sync/ui-prefs.test.ts +152 -0
  401. package/src/sync/ui-prefs.ts +135 -0
  402. package/src/sync/webdav/webdav-transport.test.ts +871 -0
  403. package/src/sync/webdav/webdav-transport.ts +535 -0
  404. package/src/ui/errors.test.ts +47 -0
  405. package/src/ui/errors.ts +100 -0
  406. package/src/ui/export-flow.test.ts +87 -0
  407. package/src/ui/export-flow.ts +156 -0
  408. package/src/ui/i18n.ts +590 -0
  409. package/src/ui/migration-consult-view.test.ts +151 -0
  410. package/src/ui/migration-consult-view.ts +178 -0
  411. package/src/ui/progress.test.ts +47 -0
  412. package/src/ui/progress.ts +102 -0
  413. package/src/ui/report.test.ts +101 -0
  414. package/src/ui/report.ts +146 -0
  415. package/src/ui/test-helpers.ts +184 -0
  416. package/src/ui/types.ts +325 -0
  417. package/src/utils/atomic-write.test.ts +312 -0
  418. package/src/utils/atomic-write.ts +422 -0
  419. package/src/utils/env-lock.test.ts +999 -0
  420. package/src/utils/env-lock.ts +1086 -0
  421. package/src/utils/hashing.ts +53 -0
  422. package/src/utils/json.ts +77 -0
  423. package/src/utils/logger.ts +114 -0
  424. package/src/utils/paths.ts +176 -0
  425. package/src/utils/proxy.test.ts +465 -0
  426. package/src/utils/proxy.ts +493 -0
  427. package/src/utils/recursive-walk.test.ts +148 -0
  428. package/src/utils/recursive-walk.ts +145 -0
  429. package/src/utils/zip.ts +332 -0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * 本地插件打包的**宿主侧**接线(T1)。
3
+ *
4
+ * 与 `local-plugin-pack.ts` 的分工:
5
+ * - `local-plugin-pack.ts` 是纯逻辑(spec 分类/路径推导/编排),零 node 子进程依赖,可离线单测;
6
+ * - 本文件只负责把真实世界接上:`npm pack` 的 execFile 包装、真实读写、临时目录生命周期。
7
+ * 放在独立文件是为了让 adapter 与 core 引擎都不需要 import `node:child_process`
8
+ * (降低被误打进 client bundle 的风险,且便于测试替身)。
9
+ */
10
+ import fsp from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ import { spawn } from 'node:child_process';
13
+ import { packLocalPlugins, parsePackOutput } from './local-plugin-pack.js';
14
+ import { quoteCmdArg } from './plugin-cli.js';
15
+ /** `npm pack` 单次调用的超时上限(慢网络/大插件留足余量,但不得无限挂起) */
16
+ const PACK_TIMEOUT_MS = 5 * 60 * 1000;
17
+ /** cmd.exe 垫片路径(Windows 上 npm 是 .cmd;与 core/plugin-cli.ts 的 spawnShim 同源做法) */
18
+ const COMSPEC = process.env.ComSpec ?? 'cmd.exe';
19
+ /**
20
+ * 默认 exec:把 `npm pack` 作为子进程执行并收集 stdout/stderr。
21
+ *
22
+ * **刻意不用 `execFile(..., { shell: true })`**:该组合会触发 Node 的 DEP0190
23
+ * (`shell:true` + args 数组只做拼接不做转义,存在注入面)。仓库既有解法是
24
+ * 在 Windows 上显式走 `cmd.exe /d /s /c "<转义后的命令行>"`(见 core/plugin-cli.ts
25
+ * 的 spawnShim + quoteCmdArg),本模块复用同一做法。
26
+ *
27
+ * 失败**不 reject**(把退出码交回调用方判定),只有 spawn 本身失败才 reject——
28
+ * 这样「npm 报错」与「npm 不存在」能被分开处理(前者是插件问题,后者是环境问题)。
29
+ */
30
+ const defaultExec = (file, args, opts) => new Promise((resolve, reject) => {
31
+ const useShim = process.platform === 'win32';
32
+ const [bin, argv] = useShim
33
+ ? [COMSPEC, ['/d', '/s', '/c', `"${[file, ...args].map(quoteCmdArg).join(' ')}"`]]
34
+ : [file, [...args]];
35
+ const child = spawn(bin, argv, {
36
+ cwd: opts.cwd,
37
+ windowsHide: true,
38
+ windowsVerbatimArguments: useShim,
39
+ shell: false, // 显式关闭:Windows 路径已由 cmd.exe 自行处理
40
+ });
41
+ let stdout = '';
42
+ let stderr = '';
43
+ let settled = false;
44
+ const timeoutMs = opts.timeoutMs ?? PACK_TIMEOUT_MS;
45
+ const timer = setTimeout(() => {
46
+ if (settled)
47
+ return;
48
+ settled = true;
49
+ child.kill();
50
+ resolve({ stdout, stderr: `${stderr}\nnpm pack 超时(${timeoutMs}ms)已终止`, code: 1 });
51
+ }, timeoutMs);
52
+ timer.unref?.();
53
+ child.stdout?.on('data', (d) => { stdout += String(d); });
54
+ child.stderr?.on('data', (d) => { stderr += String(d); });
55
+ child.on('error', (err) => {
56
+ if (settled)
57
+ return;
58
+ settled = true;
59
+ clearTimeout(timer);
60
+ reject(err); // spawn 层失败(ENOENT 等):环境问题,如实上抛
61
+ });
62
+ child.on('close', (code) => {
63
+ if (settled)
64
+ return;
65
+ settled = true;
66
+ clearTimeout(timer);
67
+ resolve({ stdout, stderr, code });
68
+ });
69
+ });
70
+ /**
71
+ * 构造注入给 `PluginsAdapter` 的打包钩子。
72
+ *
73
+ * 临时目录建在 `<dataDir>/tmp/`(与既有 tmp 语义一致),构造时不清扫、用完即删:
74
+ * 单次导出内建一个 `<dataDir>/tmp/local-pack-<random>` 目录,导出结束(无论成败)删除,
75
+ * 避免 tarball 残留在用户磁盘上。
76
+ */
77
+ export function createLocalPluginPackHook(opts) {
78
+ const exec = opts.exec ?? defaultExec;
79
+ return async (plugins, ctx) => {
80
+ // 无本地源插件时零开销返回(不建目录、不 spawn)——由 packLocalPlugins 内部短路,
81
+ // 这里先做便宜的判定避免无谓的 mkdtemp。
82
+ if (!plugins.some((p) => p.spec !== undefined && /^\s*(link|file):/i.test(p.spec))) {
83
+ return { packed: [], rewritten: {}, warnings: [] };
84
+ }
85
+ const tmpRoot = path.join(opts.dataDir, 'tmp');
86
+ let packDir;
87
+ try {
88
+ await fsp.mkdir(tmpRoot, { recursive: true });
89
+ packDir = await fsp.mkdtemp(path.join(tmpRoot, 'local-pack-'));
90
+ }
91
+ catch (err) {
92
+ return {
93
+ packed: [],
94
+ rewritten: {},
95
+ warnings: [`本地插件打包临时目录创建失败,已跳过本地插件打包: ${err instanceof Error ? err.message : String(err)}`],
96
+ };
97
+ }
98
+ try {
99
+ // profile 目录:profiles/<profile>(profile 缺省 web,与仓库既有约定一致)
100
+ const profile = ctx.profile !== undefined && ctx.profile !== '' ? ctx.profile : 'web';
101
+ return await packLocalPlugins({
102
+ plugins: plugins.map((p) => ({ name: p.name, version: p.version, spec: p.spec })),
103
+ homeDir: opts.homeDir,
104
+ profileDir: path.join(opts.homeDir, 'profiles', profile),
105
+ packDir,
106
+ exec,
107
+ readFile: (abs) => fsp.readFile(abs),
108
+ mkdir: async (abs) => { await fsp.mkdir(abs, { recursive: true }); },
109
+ timeoutMs: PACK_TIMEOUT_MS,
110
+ });
111
+ }
112
+ finally {
113
+ // 无论成败都清掉临时目录:tarball 字节已读进内存并随备份走,无需留盘
114
+ await fsp.rm(packDir, { recursive: true, force: true }).catch(() => undefined);
115
+ }
116
+ };
117
+ }
118
+ /** 供其他模块复用(避免重复实现 npm 输出解析) */
119
+ export { parsePackOutput };
120
+ //# sourceMappingURL=local-plugin-host.js.map
@@ -0,0 +1,135 @@
1
+ /** 本地源插件 tarball 在 ZIP 内的存放目录(相对分区前缀之下,如 plugin-files/ 之下) */
2
+ export declare const LOCAL_PLUGIN_DIR = "local-plugins";
3
+ /** 单插件 tarball 体积上限(超过则跳过并告警,防异常巨大的插件撑爆备份) */
4
+ export declare const MAX_LOCAL_TARBALL_BYTES: number;
5
+ /** 依赖 spec 的来源类别 */
6
+ export type LocalPluginSourceKind = 'link' | 'file' | 'registry' | 'git' | 'other';
7
+ /**
8
+ * 分类依赖 spec。
9
+ * - `link:` → 'link'(本地目录)
10
+ * - `file:` → 'file'(本地 tarball / 目录)
11
+ * - `github:` / `gitlab:` / `bitbucket:` / `git+` / `http(s):` → 'git'
12
+ * - 空 / `workspace:` / 版本区间(`^1.2.3`、`~1.2`、`1.2.3`、`latest`、`*`)→ 'registry'
13
+ * - 其余 → 'other'
14
+ */
15
+ export declare function classifyPluginSpec(spec: string | undefined): LocalPluginSourceKind;
16
+ /** 是否本地源(换机后必然不可达,需打包) */
17
+ export declare function isLocalPluginSpec(spec: string | undefined): boolean;
18
+ /**
19
+ * 包名 → 归档内安全的文件名片段。
20
+ * `@scope/name` → `@scope__name`(斜杠被替换,**结果绝不含 `/` 或 `\`**)。
21
+ * 其余非法字符(路径分隔符、`:`、空白)一并折叠为 `-`。
22
+ */
23
+ export declare function safePackageFileFragment(pkgName: string): string;
24
+ /** tarball 在归档内的相对路径(一律 `local-plugins/<fragment>.tgz`,pkgName 已脱离 version) */
25
+ export declare function tarballNameFor(pkgName: string, version: string): string;
26
+ /**
27
+ * 本地 spec → 绝对路径。
28
+ * - `link:D:/x` / `link:/abs/x` → 绝对路径原样(规范化分隔符);
29
+ * - `link:./x` / `link:../x` → 相对 profileDir 解析;
30
+ * - `~` 开头 → 相对 homeDir 展开;
31
+ * - 裸路径(无前缀,如 `./plugin`)→ 同样按相对 profileDir 处理。
32
+ *
33
+ * 平台无关:不读 `process.platform`,交给 `path.resolve` / `path.isAbsolute`。
34
+ */
35
+ export declare function resolveLocalPluginPath(spec: string, opts: {
36
+ homeDir: string;
37
+ profileDir: string;
38
+ }): string;
39
+ /**
40
+ * 把不可移植的本地 spec 重写成可移植形式。
41
+ * 幂等:对已是 `file:` 前缀的输入再次调用不改变语义(前缀保留、路径部分原样)。
42
+ */
43
+ export declare function rewriteLocalSpec(spec: string, opts: {
44
+ tarballRel: string;
45
+ }): string;
46
+ /**
47
+ * 判断一个 spec 是否已是「重写后的可移植形式」(`file:local-plugins/...`)。
48
+ * 导入端据此避免二次重写。
49
+ */
50
+ export declare function isPackedLocalSpec(spec: string | undefined): boolean;
51
+ /** 打包所需的插件最小信息(只依赖 core/types.ts 的 PluginInfo 子集,便于测试构造) */
52
+ export interface LocalPluginCandidate {
53
+ name: string;
54
+ version: string;
55
+ spec?: string;
56
+ }
57
+ /** 命令执行结果(与 child_process 的 execFile 回调对齐;不 import node:child_process) */
58
+ export interface PackExecResult {
59
+ stdout: string;
60
+ stderr: string;
61
+ code: number | null;
62
+ }
63
+ /** 命令执行器签名:`exec(file, args, opts)` → Promise */
64
+ export type PackExec = (file: string, args: string[], opts: {
65
+ cwd: string;
66
+ timeoutMs?: number;
67
+ }) => Promise<PackExecResult>;
68
+ export interface PackLocalPluginsOptions {
69
+ plugins: readonly LocalPluginCandidate[];
70
+ /** $DSH_HOME 绝对路径 */
71
+ homeDir: string;
72
+ /** profile 目录绝对路径(profiles/<name>) */
73
+ profileDir: string;
74
+ /** tarball 临时产物目录(调用方保证存在或由本函数创建) */
75
+ packDir: string;
76
+ /** 命令执行器(注入;生产传 npm pack 的 execFile 包装) */
77
+ exec: PackExec;
78
+ /** 读文件(注入,便于测试)——只在打包成功后调用 */
79
+ readFile: (absPath: string) => Promise<Uint8Array>;
80
+ /** 确保目录存在(注入) */
81
+ mkdir: (absDir: string) => Promise<void>;
82
+ /** 目标平台校验(可选;'win32' 时 npm 是 .cmd 垫片,调用方负责 exec 形态) */
83
+ timeoutMs?: number;
84
+ /** tarball 体积上限(缺省 MAX_LOCAL_TARBALL_BYTES) */
85
+ maxTarballBytes?: number;
86
+ /** 是否把 `npm pack` 的 stderr 作为告警捕获(缺省 true) */
87
+ collectStderr?: boolean;
88
+ }
89
+ /** 单个本地插件的打包结果 */
90
+ export interface PackedLocalPlugin {
91
+ packageName: string;
92
+ version: string;
93
+ /** 归档内相对路径(`local-plugins/<x>.tgz`,正斜杠) */
94
+ relativePath: string;
95
+ /** 重写后的 spec(`file:local-plugins/<x>.tgz`) */
96
+ rewrittenSpec: string;
97
+ data: Uint8Array;
98
+ }
99
+ export interface PackLocalPluginsResult {
100
+ packed: PackedLocalPlugin[];
101
+ /** packageName → 重写后的 spec */
102
+ rewritten: Record<string, string>;
103
+ /** 非致命告警(跳过原因等;调用方并入分区 warnings) */
104
+ warnings: string[];
105
+ }
106
+ /**
107
+ * 从 `npm pack --json` 的 stdout 解析出 tarball 文件名。
108
+ * 兼容三种形态:
109
+ * 1. `--json` 数组:`[{"filename":"x.tgz", ...}]` → 取 filename
110
+ * 2. 旧版单对象:`{"filename":"x.tgz"}` → 取 filename
111
+ * 3. 纯文本:末行非空即文件名(兜底)
112
+ * 解析失败返回 null(调用方按文件名约定回退)。
113
+ */
114
+ export declare function parsePackOutput(stdout: string, expectedPkg: string, version: string): string | null;
115
+ /**
116
+ * 对全部本地源插件执行打包。
117
+ *
118
+ * 行为要点:
119
+ * - 只处理 `isLocalPluginSpec(spec)` 的插件;非本地源直接跳过(**不产生告警**,属正常);
120
+ * - 每个插件独立 try/catch:任一步失败 → 该插件进 warnings,**其余继续**;
121
+ * - 目录不存在 / 非绝对可达 → 告警跳过(不抛错);
122
+ * - 成功产出 `packed`(含字节)与 `rewritten` 映射。
123
+ */
124
+ export declare function packLocalPlugins(opts: PackLocalPluginsOptions): Promise<PackLocalPluginsResult>;
125
+ /**
126
+ * 判断 spec 是否是「裸本地路径」(无任何前缀,如 `./plugin`、`D:\dev\x`)。
127
+ * 这类 spec 在 DSH profile 中不常见(pnpm 侧通常要求 `link:` / `file:` 前缀),
128
+ * 但一旦出现,同样指向本机路径、换机后不可达。
129
+ *
130
+ * **当前无生产调用点**(`packLocalPlugins` 目前只处理带前缀的 spec)——这是**有意的**
131
+ * 预留扩展点,不是死代码:保留它可让「无前缀裸路径」这一已识别的边界有明确表达,
132
+ * 后续若要在 `packLocalPlugins` 中一并处理,直接复用即可。
133
+ * (若哪天决定不支持该形态,请连同本注释与对应测试一起删除。)
134
+ */
135
+ export declare function isBareLocalPath(spec: string | undefined): boolean;
@@ -0,0 +1,278 @@
1
+ /**
2
+ * 本地源插件打包(T1):让 `link:` / `file:` 来源的插件能真正跨机器迁移。
3
+ *
4
+ * 背景(真实缺陷):`src/adapters/plugins.ts` 会把 profile package.json 的依赖 spec
5
+ * 原样记进 `PluginEntry.spec`,其中 `link:D:/path`、`file:./x.tgz` 指向**本机路径**——
6
+ * 目标机器上该路径根本不存在,导入时 `pnpm add link:/...` 必然失败,插件被静默丢失。
7
+ * 本机实例:`web` profile 的 `"dsh-config-manager": "link:D:/Projects/personal/dsh-config-manager"`。
8
+ *
9
+ * 解法:导出时对本地源插件执行 `npm pack`,把产出的 tarball 一并放进备份;
10
+ * 导入时用「解包后的 tarball 绝对路径」重写 spec,走 `file:<abs>` 安装(pnpm 原生支持)。
11
+ *
12
+ * 本模块前半部分是**纯函数**(零 IO、零依赖,只用于 spec 分类与路径推导,node 可直测);
13
+ * 后半部分是**打包编排**(`exec` 注入,测试用假 exec 即可离线驱动,不绑真实 npm)。
14
+ *
15
+ * 安全与资源约束:
16
+ * - 只打包 `npm pack` 的产物,**绝不**递归整个插件目录(node_modules 由 npm pack 自身排除);
17
+ * - tarball 体积超上限即跳过并告警(防单个插件把备份撑爆);
18
+ * - 归档内相对路径一律 `local-plugins/<name>.tgz`,**不含任何路径分隔符**(防穿越);
19
+ * - 单个插件打包失败**不中断导出**,只记 warning(与「单分区失败不拖垮整体」同语义)。
20
+ */
21
+ import path from 'node:path';
22
+ /* ---------------- 常量 ---------------- */
23
+ /** 本地源插件 tarball 在 ZIP 内的存放目录(相对分区前缀之下,如 plugin-files/ 之下) */
24
+ export const LOCAL_PLUGIN_DIR = 'local-plugins';
25
+ /** 单插件 tarball 体积上限(超过则跳过并告警,防异常巨大的插件撑爆备份) */
26
+ export const MAX_LOCAL_TARBALL_BYTES = 100 * 1024 * 1024;
27
+ /** 本地源 spec 前缀(大小写不敏感匹配后使用小写前缀做判定) */
28
+ const LINK_PREFIX = 'link:';
29
+ const FILE_PREFIX = 'file:';
30
+ /** 非 registry 来源前缀(与 core/plugin-cli.ts 的 NON_REGISTRY_SPEC 语义对齐) */
31
+ const GIT_PREFIXES = ['github:', 'gitlab:', 'bitbucket:', 'git+', 'http:', 'https:'];
32
+ /** spec 前缀判定:大小写不敏感 + 首尾空白容忍(`Link:` / ` link:` 都能识别)。 */
33
+ function startsWithIgnoreCase(spec, prefix) {
34
+ return spec.slice(0, prefix.length).toLowerCase() === prefix;
35
+ }
36
+ /**
37
+ * 分类依赖 spec。
38
+ * - `link:` → 'link'(本地目录)
39
+ * - `file:` → 'file'(本地 tarball / 目录)
40
+ * - `github:` / `gitlab:` / `bitbucket:` / `git+` / `http(s):` → 'git'
41
+ * - 空 / `workspace:` / 版本区间(`^1.2.3`、`~1.2`、`1.2.3`、`latest`、`*`)→ 'registry'
42
+ * - 其余 → 'other'
43
+ */
44
+ export function classifyPluginSpec(spec) {
45
+ if (typeof spec !== 'string')
46
+ return 'registry';
47
+ const s = spec.trim();
48
+ if (s === '')
49
+ return 'registry';
50
+ if (startsWithIgnoreCase(s, LINK_PREFIX))
51
+ return 'link';
52
+ if (startsWithIgnoreCase(s, FILE_PREFIX))
53
+ return 'file';
54
+ for (const p of GIT_PREFIXES) {
55
+ if (startsWithIgnoreCase(s, p))
56
+ return 'git';
57
+ }
58
+ if (startsWithIgnoreCase(s, 'workspace:'))
59
+ return 'registry';
60
+ return 'registry';
61
+ }
62
+ /** 是否本地源(换机后必然不可达,需打包) */
63
+ export function isLocalPluginSpec(spec) {
64
+ const kind = classifyPluginSpec(spec);
65
+ return kind === 'link' || kind === 'file';
66
+ }
67
+ /**
68
+ * spec 是否带显式前缀(`link:` / `file:` 等)。
69
+ *
70
+ * 注意:**Windows 盘符不是 scheme**。`C:\dev\x` 含 `:` 但那是单字母盘符后跟路径分隔符,
71
+ * 必须排除,否则裸盘符路径会被误判为「已有 scheme」而绕过 `file:` 补全。
72
+ */
73
+ function hasSchemePrefix(spec) {
74
+ if (/^[a-zA-Z]:[\\/]/.test(spec))
75
+ return false; // 盘符路径,不是 scheme
76
+ return /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(spec); // RFC 3986 scheme
77
+ }
78
+ /* ---------------- 纯函数:路径与命名 ---------------- */
79
+ /**
80
+ * 包名 → 归档内安全的文件名片段。
81
+ * `@scope/name` → `@scope__name`(斜杠被替换,**结果绝不含 `/` 或 `\`**)。
82
+ * 其余非法字符(路径分隔符、`:`、空白)一并折叠为 `-`。
83
+ */
84
+ export function safePackageFileFragment(pkgName) {
85
+ const replaced = pkgName.replace(/[/\\]/g, '__');
86
+ return replaced.replace(/[^A-Za-z0-9@._-]/g, '-');
87
+ }
88
+ /** tarball 在归档内的相对路径(一律 `local-plugins/<fragment>.tgz`,pkgName 已脱离 version) */
89
+ export function tarballNameFor(pkgName, version) {
90
+ const safeVersion = version.replace(/[^A-Za-z0-9._-]/g, '-');
91
+ const fragment = safePackageFileFragment(pkgName);
92
+ const withVersion = safeVersion === '' ? fragment : `${fragment}-${safeVersion}`;
93
+ return `${LOCAL_PLUGIN_DIR}/${withVersion}.tgz`;
94
+ }
95
+ /**
96
+ * 本地 spec → 绝对路径。
97
+ * - `link:D:/x` / `link:/abs/x` → 绝对路径原样(规范化分隔符);
98
+ * - `link:./x` / `link:../x` → 相对 profileDir 解析;
99
+ * - `~` 开头 → 相对 homeDir 展开;
100
+ * - 裸路径(无前缀,如 `./plugin`)→ 同样按相对 profileDir 处理。
101
+ *
102
+ * 平台无关:不读 `process.platform`,交给 `path.resolve` / `path.isAbsolute`。
103
+ */
104
+ export function resolveLocalPluginPath(spec, opts) {
105
+ let raw = spec.trim();
106
+ if (startsWithIgnoreCase(raw, LINK_PREFIX))
107
+ raw = raw.slice(LINK_PREFIX.length);
108
+ else if (startsWithIgnoreCase(raw, FILE_PREFIX))
109
+ raw = raw.slice(FILE_PREFIX.length);
110
+ raw = raw.trim();
111
+ // `~` 展开(`~/x` 或 `~`)
112
+ if (raw === '~')
113
+ return opts.homeDir;
114
+ if (raw.startsWith('~/') || raw.startsWith('~\\')) {
115
+ return path.resolve(opts.homeDir, raw.slice(2));
116
+ }
117
+ // 绝对路径(含 Windows 盘符 / UNC)
118
+ if (path.isAbsolute(raw))
119
+ return path.normalize(raw);
120
+ // 相对路径 → 以 profileDir 为基准
121
+ return path.resolve(opts.profileDir, raw);
122
+ }
123
+ /**
124
+ * 把不可移植的本地 spec 重写成可移植形式。
125
+ * 幂等:对已是 `file:` 前缀的输入再次调用不改变语义(前缀保留、路径部分原样)。
126
+ */
127
+ export function rewriteLocalSpec(spec, opts) {
128
+ void spec;
129
+ const rel = opts.tarballRel.replace(/\\/g, '/');
130
+ return `${FILE_PREFIX}${rel}`;
131
+ }
132
+ /**
133
+ * 判断一个 spec 是否已是「重写后的可移植形式」(`file:local-plugins/...`)。
134
+ * 导入端据此避免二次重写。
135
+ */
136
+ export function isPackedLocalSpec(spec) {
137
+ if (typeof spec !== 'string')
138
+ return false;
139
+ const s = spec.trim();
140
+ if (!startsWithIgnoreCase(s, FILE_PREFIX))
141
+ return false;
142
+ const rest = s.slice(FILE_PREFIX.length).replace(/\\/g, '/');
143
+ return rest.startsWith(`${LOCAL_PLUGIN_DIR}/`);
144
+ }
145
+ /**
146
+ * 从 `npm pack --json` 的 stdout 解析出 tarball 文件名。
147
+ * 兼容三种形态:
148
+ * 1. `--json` 数组:`[{"filename":"x.tgz", ...}]` → 取 filename
149
+ * 2. 旧版单对象:`{"filename":"x.tgz"}` → 取 filename
150
+ * 3. 纯文本:末行非空即文件名(兜底)
151
+ * 解析失败返回 null(调用方按文件名约定回退)。
152
+ */
153
+ export function parsePackOutput(stdout, expectedPkg, version) {
154
+ const text = stdout.trim();
155
+ if (text === '')
156
+ return null;
157
+ // 形态 1/2:JSON(可能与 npm 的进度日志混排,故从首个 '[' / '{' 起截取)
158
+ const jsonStart = Math.min(...[text.indexOf('['), text.indexOf('{')].filter((i) => i >= 0));
159
+ if (Number.isFinite(jsonStart)) {
160
+ try {
161
+ const parsed = JSON.parse(text.slice(jsonStart));
162
+ const first = Array.isArray(parsed) ? parsed[0] : parsed;
163
+ if (first !== null && typeof first === 'object') {
164
+ const filename = first.filename;
165
+ if (typeof filename === 'string' && filename !== '')
166
+ return filename;
167
+ }
168
+ }
169
+ catch {
170
+ /* 落到文本兜底 */
171
+ }
172
+ }
173
+ // 形态 3:纯文本,取最后一行非空
174
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l !== '');
175
+ const last = lines[lines.length - 1];
176
+ if (last !== undefined && last.endsWith('.tgz'))
177
+ return last;
178
+ // 最终兜底:按 npm pack 的命名约定推导
179
+ const fragment = safePackageFileFragment(expectedPkg).replace(/^@/, '').replace(/__/g, '-');
180
+ return `${fragment}-${version}.tgz`;
181
+ }
182
+ /**
183
+ * 对全部本地源插件执行打包。
184
+ *
185
+ * 行为要点:
186
+ * - 只处理 `isLocalPluginSpec(spec)` 的插件;非本地源直接跳过(**不产生告警**,属正常);
187
+ * - 每个插件独立 try/catch:任一步失败 → 该插件进 warnings,**其余继续**;
188
+ * - 目录不存在 / 非绝对可达 → 告警跳过(不抛错);
189
+ * - 成功产出 `packed`(含字节)与 `rewritten` 映射。
190
+ */
191
+ export async function packLocalPlugins(opts) {
192
+ const packed = [];
193
+ const rewritten = {};
194
+ const warnings = [];
195
+ const maxBytes = opts.maxTarballBytes ?? MAX_LOCAL_TARBALL_BYTES;
196
+ const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1000;
197
+ const candidates = opts.plugins.filter((p) => isLocalPluginSpec(p.spec));
198
+ if (candidates.length === 0)
199
+ return { packed, rewritten, warnings };
200
+ try {
201
+ await opts.mkdir(opts.packDir);
202
+ }
203
+ catch (err) {
204
+ warnings.push(`本地插件打包目录创建失败,已跳过全部本地插件: ${reasonOf(err)}`);
205
+ return { packed, rewritten, warnings };
206
+ }
207
+ for (const plugin of candidates) {
208
+ const spec = plugin.spec;
209
+ try {
210
+ const sourcePath = resolveLocalPluginPath(spec, {
211
+ homeDir: opts.homeDir,
212
+ profileDir: opts.profileDir,
213
+ });
214
+ // cwd 设为插件目录 + `npm pack . --pack-destination <abs>`:跨 npm 版本最稳的形态
215
+ const result = await opts.exec('npm', ['pack', '.', '--pack-destination', opts.packDir], {
216
+ cwd: sourcePath,
217
+ timeoutMs,
218
+ });
219
+ if (result.code !== 0) {
220
+ warnings.push(`本地插件 ${plugin.name} 打包失败(npm pack 退出码 ${result.code ?? 'null'}):${firstLine(result.stderr) || firstLine(result.stdout) || '无输出'}`);
221
+ continue;
222
+ }
223
+ const filename = parsePackOutput(result.stdout, plugin.name, plugin.version);
224
+ if (filename === null) {
225
+ warnings.push(`本地插件 ${plugin.name} 打包失败:无法从 npm pack 输出解析文件名`);
226
+ continue;
227
+ }
228
+ const tgzPath = path.join(opts.packDir, filename);
229
+ const data = await opts.readFile(tgzPath);
230
+ if (data.byteLength > maxBytes) {
231
+ warnings.push(`本地插件 ${plugin.name} 的 tarball 超过上限(${data.byteLength} > ${maxBytes} 字节),已跳过打包`);
232
+ continue;
233
+ }
234
+ const relativePath = tarballNameFor(plugin.name, plugin.version);
235
+ const rewrittenSpec = rewriteLocalSpec(spec, { tarballRel: relativePath });
236
+ packed.push({
237
+ packageName: plugin.name,
238
+ version: plugin.version,
239
+ relativePath,
240
+ rewrittenSpec,
241
+ data,
242
+ });
243
+ rewritten[plugin.name] = rewrittenSpec;
244
+ }
245
+ catch (err) {
246
+ warnings.push(`本地插件 ${plugin.name} 打包失败:${reasonOf(err)}`);
247
+ }
248
+ }
249
+ return { packed, rewritten, warnings };
250
+ }
251
+ function reasonOf(err) {
252
+ return err instanceof Error ? err.message : String(err);
253
+ }
254
+ function firstLine(s) {
255
+ const l = s.split(/\r?\n/).find((x) => x.trim() !== '');
256
+ return l === undefined ? '' : l.trim();
257
+ }
258
+ /**
259
+ * 判断 spec 是否是「裸本地路径」(无任何前缀,如 `./plugin`、`D:\dev\x`)。
260
+ * 这类 spec 在 DSH profile 中不常见(pnpm 侧通常要求 `link:` / `file:` 前缀),
261
+ * 但一旦出现,同样指向本机路径、换机后不可达。
262
+ *
263
+ * **当前无生产调用点**(`packLocalPlugins` 目前只处理带前缀的 spec)——这是**有意的**
264
+ * 预留扩展点,不是死代码:保留它可让「无前缀裸路径」这一已识别的边界有明确表达,
265
+ * 后续若要在 `packLocalPlugins` 中一并处理,直接复用即可。
266
+ * (若哪天决定不支持该形态,请连同本注释与对应测试一起删除。)
267
+ */
268
+ export function isBareLocalPath(spec) {
269
+ if (typeof spec !== 'string')
270
+ return false;
271
+ const s = spec.trim();
272
+ if (s === '')
273
+ return false;
274
+ if (hasSchemePrefix(s))
275
+ return false;
276
+ return s.startsWith('./') || s.startsWith('../') || s.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(s);
277
+ }
278
+ //# sourceMappingURL=local-plugin-pack.js.map