@barryfan2045/dsh-matrix 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +286 -0
  3. package/THIRD_PARTY_NOTICES.md +53 -0
  4. package/assets/avatars/instance-01.png +0 -0
  5. package/assets/avatars/instance-02.png +0 -0
  6. package/assets/avatars/instance-03.png +0 -0
  7. package/assets/avatars/instance-04.png +0 -0
  8. package/assets/avatars/instance-05.png +0 -0
  9. package/assets/avatars/instance-06.png +0 -0
  10. package/assets/avatars/instance-07.png +0 -0
  11. package/assets/avatars/instance-08.png +0 -0
  12. package/assets/avatars/instance-09.png +0 -0
  13. package/assets/avatars/instance-10.png +0 -0
  14. package/assets/avatars/instance-11.png +0 -0
  15. package/assets/avatars/instance-12.png +0 -0
  16. package/assets/avatars/management.png +0 -0
  17. package/cordis.patch.yml +13 -0
  18. package/dist/auth/admin-commands.d.ts +47 -0
  19. package/dist/auth/admin-commands.d.ts.map +1 -0
  20. package/dist/auth/admin-commands.js +112 -0
  21. package/dist/auth/admin-commands.js.map +1 -0
  22. package/dist/auth/challenge.d.ts +93 -0
  23. package/dist/auth/challenge.d.ts.map +1 -0
  24. package/dist/auth/challenge.js +104 -0
  25. package/dist/auth/challenge.js.map +1 -0
  26. package/dist/auth/store.d.ts +55 -0
  27. package/dist/auth/store.d.ts.map +1 -0
  28. package/dist/auth/store.js +124 -0
  29. package/dist/auth/store.js.map +1 -0
  30. package/dist/bridge/agent-commands.d.ts +39 -0
  31. package/dist/bridge/agent-commands.d.ts.map +1 -0
  32. package/dist/bridge/agent-commands.js +243 -0
  33. package/dist/bridge/agent-commands.js.map +1 -0
  34. package/dist/bridge/attachments-ledger.d.ts +54 -0
  35. package/dist/bridge/attachments-ledger.d.ts.map +1 -0
  36. package/dist/bridge/attachments-ledger.js +68 -0
  37. package/dist/bridge/attachments-ledger.js.map +1 -0
  38. package/dist/bridge/commands.d.ts +28 -0
  39. package/dist/bridge/commands.d.ts.map +1 -0
  40. package/dist/bridge/commands.js +18 -0
  41. package/dist/bridge/commands.js.map +1 -0
  42. package/dist/bridge/crypto-health.d.ts +37 -0
  43. package/dist/bridge/crypto-health.d.ts.map +1 -0
  44. package/dist/bridge/crypto-health.js +38 -0
  45. package/dist/bridge/crypto-health.js.map +1 -0
  46. package/dist/bridge/group-gate.d.ts +38 -0
  47. package/dist/bridge/group-gate.d.ts.map +1 -0
  48. package/dist/bridge/group-gate.js +49 -0
  49. package/dist/bridge/group-gate.js.map +1 -0
  50. package/dist/bridge/instance-lock.d.ts +10 -0
  51. package/dist/bridge/instance-lock.d.ts.map +1 -0
  52. package/dist/bridge/instance-lock.js +82 -0
  53. package/dist/bridge/instance-lock.js.map +1 -0
  54. package/dist/bridge/interactions.d.ts +62 -0
  55. package/dist/bridge/interactions.d.ts.map +1 -0
  56. package/dist/bridge/interactions.js +237 -0
  57. package/dist/bridge/interactions.js.map +1 -0
  58. package/dist/bridge/management-room.d.ts +13 -0
  59. package/dist/bridge/management-room.d.ts.map +1 -0
  60. package/dist/bridge/management-room.js +27 -0
  61. package/dist/bridge/management-room.js.map +1 -0
  62. package/dist/bridge/multi-project.d.ts +34 -0
  63. package/dist/bridge/multi-project.d.ts.map +1 -0
  64. package/dist/bridge/multi-project.js +44 -0
  65. package/dist/bridge/multi-project.js.map +1 -0
  66. package/dist/bridge/pipeline.d.ts +151 -0
  67. package/dist/bridge/pipeline.d.ts.map +1 -0
  68. package/dist/bridge/pipeline.js +502 -0
  69. package/dist/bridge/pipeline.js.map +1 -0
  70. package/dist/bridge/pmctl.d.ts +50 -0
  71. package/dist/bridge/pmctl.d.ts.map +1 -0
  72. package/dist/bridge/pmctl.js +272 -0
  73. package/dist/bridge/pmctl.js.map +1 -0
  74. package/dist/bridge/power-levels.d.ts +57 -0
  75. package/dist/bridge/power-levels.d.ts.map +1 -0
  76. package/dist/bridge/power-levels.js +92 -0
  77. package/dist/bridge/power-levels.js.map +1 -0
  78. package/dist/bridge/power-sync.d.ts +64 -0
  79. package/dist/bridge/power-sync.d.ts.map +1 -0
  80. package/dist/bridge/power-sync.js +121 -0
  81. package/dist/bridge/power-sync.js.map +1 -0
  82. package/dist/bridge/projects.d.ts +97 -0
  83. package/dist/bridge/projects.d.ts.map +1 -0
  84. package/dist/bridge/projects.js +288 -0
  85. package/dist/bridge/projects.js.map +1 -0
  86. package/dist/bridge/quote-cache.d.ts +38 -0
  87. package/dist/bridge/quote-cache.d.ts.map +1 -0
  88. package/dist/bridge/quote-cache.js +56 -0
  89. package/dist/bridge/quote-cache.js.map +1 -0
  90. package/dist/bridge/reply.d.ts +31 -0
  91. package/dist/bridge/reply.d.ts.map +1 -0
  92. package/dist/bridge/reply.js +122 -0
  93. package/dist/bridge/reply.js.map +1 -0
  94. package/dist/bridge/space-identity.d.ts +43 -0
  95. package/dist/bridge/space-identity.d.ts.map +1 -0
  96. package/dist/bridge/space-identity.js +92 -0
  97. package/dist/bridge/space-identity.js.map +1 -0
  98. package/dist/bridge/space-sync.d.ts +94 -0
  99. package/dist/bridge/space-sync.d.ts.map +1 -0
  100. package/dist/bridge/space-sync.js +137 -0
  101. package/dist/bridge/space-sync.js.map +1 -0
  102. package/dist/bridge/turn-commands.d.ts +28 -0
  103. package/dist/bridge/turn-commands.d.ts.map +1 -0
  104. package/dist/bridge/turn-commands.js +97 -0
  105. package/dist/bridge/turn-commands.js.map +1 -0
  106. package/dist/cli/config-store.d.ts +32 -0
  107. package/dist/cli/config-store.d.ts.map +1 -0
  108. package/dist/cli/config-store.js +118 -0
  109. package/dist/cli/config-store.js.map +1 -0
  110. package/dist/cli/crypto-store.d.ts +42 -0
  111. package/dist/cli/crypto-store.d.ts.map +1 -0
  112. package/dist/cli/crypto-store.js +70 -0
  113. package/dist/cli/crypto-store.js.map +1 -0
  114. package/dist/cli/device-prune.d.ts +105 -0
  115. package/dist/cli/device-prune.d.ts.map +1 -0
  116. package/dist/cli/device-prune.js +163 -0
  117. package/dist/cli/device-prune.js.map +1 -0
  118. package/dist/cli/index.d.ts +4 -0
  119. package/dist/cli/index.d.ts.map +1 -0
  120. package/dist/cli/index.js +584 -0
  121. package/dist/cli/index.js.map +1 -0
  122. package/dist/cli/log-filter.d.ts +32 -0
  123. package/dist/cli/log-filter.d.ts.map +1 -0
  124. package/dist/cli/log-filter.js +40 -0
  125. package/dist/cli/log-filter.js.map +1 -0
  126. package/dist/cli/pnpm-builds.d.ts +32 -0
  127. package/dist/cli/pnpm-builds.d.ts.map +1 -0
  128. package/dist/cli/pnpm-builds.js +73 -0
  129. package/dist/cli/pnpm-builds.js.map +1 -0
  130. package/dist/cli/profile-patch.d.ts +26 -0
  131. package/dist/cli/profile-patch.d.ts.map +1 -0
  132. package/dist/cli/profile-patch.js +56 -0
  133. package/dist/cli/profile-patch.js.map +1 -0
  134. package/dist/cli/setup.d.ts +39 -0
  135. package/dist/cli/setup.d.ts.map +1 -0
  136. package/dist/cli/setup.js +234 -0
  137. package/dist/cli/setup.js.map +1 -0
  138. package/dist/cli/systemd-hint.d.ts +11 -0
  139. package/dist/cli/systemd-hint.d.ts.map +1 -0
  140. package/dist/cli/systemd-hint.js +46 -0
  141. package/dist/cli/systemd-hint.js.map +1 -0
  142. package/dist/cli/systemd.d.ts +32 -0
  143. package/dist/cli/systemd.d.ts.map +1 -0
  144. package/dist/cli/systemd.js +58 -0
  145. package/dist/cli/systemd.js.map +1 -0
  146. package/dist/config.d.ts +54 -0
  147. package/dist/config.d.ts.map +1 -0
  148. package/dist/config.js +107 -0
  149. package/dist/config.js.map +1 -0
  150. package/dist/dsh/backend.d.ts +110 -0
  151. package/dist/dsh/backend.d.ts.map +1 -0
  152. package/dist/dsh/backend.js +481 -0
  153. package/dist/dsh/backend.js.map +1 -0
  154. package/dist/dsh/interactions.d.ts +50 -0
  155. package/dist/dsh/interactions.d.ts.map +1 -0
  156. package/dist/dsh/interactions.js +141 -0
  157. package/dist/dsh/interactions.js.map +1 -0
  158. package/dist/dsh/stream.d.ts +32 -0
  159. package/dist/dsh/stream.d.ts.map +1 -0
  160. package/dist/dsh/stream.js +109 -0
  161. package/dist/dsh/stream.js.map +1 -0
  162. package/dist/formatting.d.ts +14 -0
  163. package/dist/formatting.d.ts.map +1 -0
  164. package/dist/formatting.js +63 -0
  165. package/dist/formatting.js.map +1 -0
  166. package/dist/index.d.ts +8 -0
  167. package/dist/index.d.ts.map +1 -0
  168. package/dist/index.js +232 -0
  169. package/dist/index.js.map +1 -0
  170. package/dist/logger.d.ts +9 -0
  171. package/dist/logger.d.ts.map +1 -0
  172. package/dist/logger.js +28 -0
  173. package/dist/logger.js.map +1 -0
  174. package/dist/matrix/matrix-events.d.ts +49 -0
  175. package/dist/matrix/matrix-events.d.ts.map +1 -0
  176. package/dist/matrix/matrix-events.js +89 -0
  177. package/dist/matrix/matrix-events.js.map +1 -0
  178. package/dist/matrix/transport.d.ts +192 -0
  179. package/dist/matrix/transport.d.ts.map +1 -0
  180. package/dist/matrix/transport.js +510 -0
  181. package/dist/matrix/transport.js.map +1 -0
  182. package/dist/ports/attachments.d.ts +51 -0
  183. package/dist/ports/attachments.d.ts.map +1 -0
  184. package/dist/ports/attachments.js +27 -0
  185. package/dist/ports/attachments.js.map +1 -0
  186. package/dist/ports/backend.d.ts +159 -0
  187. package/dist/ports/backend.d.ts.map +1 -0
  188. package/dist/ports/backend.js +25 -0
  189. package/dist/ports/backend.js.map +1 -0
  190. package/dist/ports/interactions.d.ts +65 -0
  191. package/dist/ports/interactions.d.ts.map +1 -0
  192. package/dist/ports/interactions.js +15 -0
  193. package/dist/ports/interactions.js.map +1 -0
  194. package/dist/ports/streaming.d.ts +45 -0
  195. package/dist/ports/streaming.d.ts.map +1 -0
  196. package/dist/ports/streaming.js +23 -0
  197. package/dist/ports/streaming.js.map +1 -0
  198. package/dist/ports/transport.d.ts +68 -0
  199. package/dist/ports/transport.d.ts.map +1 -0
  200. package/dist/ports/transport.js +2 -0
  201. package/dist/ports/transport.js.map +1 -0
  202. package/dist/types.d.ts +53 -0
  203. package/dist/types.d.ts.map +1 -0
  204. package/dist/types.js +2 -0
  205. package/dist/types.js.map +1 -0
  206. package/package.json +80 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `dsh-matrix logs/status` 的 journalctl argv 构建(纯函数)。
3
+ *
4
+ * 移植自 pi-courier src/log-filter.ts(spec #34),改语义:
5
+ * dsh-matrix 桥是单实例、日志行没有项目标签(logger 输出格式为
6
+ * `[ISO时间] [LEVEL] 正文`),故去掉 requestedProjects/availableLabels,
7
+ * 只保留 --level 阈值过滤。级别过滤原理不变:journalctl 不解析自定义字段,
8
+ * `-p` 在 StandardOutput=inherit 下无效,必须用 `--grep` 锚定行内 `[LEVEL] `。
9
+ * 要求 journalctl 带 PCRE2(`--grep`/`--case`);缺失时由 journalctl 报错,
10
+ * 不做静默降级。
11
+ */
12
+ /** 下限阈值:`--level X` 显示 X 及更高级别。 */
13
+ const LEVEL_ALTERNATIONS = {
14
+ debug: 'DEBUG|INFO|WARN|ERROR',
15
+ info: 'INFO|WARN|ERROR',
16
+ warn: 'WARN|ERROR',
17
+ error: 'ERROR',
18
+ };
19
+ export function parseFilterLevel(value) {
20
+ return value === 'debug' || value === 'info' || value === 'warn' || value === 'error'
21
+ ? value
22
+ : null;
23
+ }
24
+ export function buildLogFilterArgs(req) {
25
+ const args = ['--user', '-u', req.unit];
26
+ if (req.follow)
27
+ args.push('-f');
28
+ const level = parseFilterLevel(req.level);
29
+ if (!level) {
30
+ return { ok: false, message: `未知日志级别: ${req.level}(可选: debug / info / warn / error)` };
31
+ }
32
+ // level=debug 时不加 grep:全量放行(debug 阈值本就包含所有行)。
33
+ if (level !== 'debug') {
34
+ args.push('--grep', `\\[(${LEVEL_ALTERNATIONS[level]})\\] `, '--case=0');
35
+ }
36
+ if (req.lineCount !== undefined)
37
+ args.push('-n', String(req.lineCount));
38
+ return { ok: true, args };
39
+ }
40
+ //# sourceMappingURL=log-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-filter.js","sourceRoot":"","sources":["../../src/cli/log-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,mCAAmC;AACnC,MAAM,kBAAkB,GAAgC;IACtD,KAAK,EAAE,uBAAuB;IAC9B,IAAI,EAAE,iBAAiB;IACvB,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,OAAO;CACf,CAAA;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO;QACnF,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,IAAI,CAAA;AACV,CAAC;AAeD,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,MAAM,IAAI,GAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;IACjD,IAAI,GAAG,CAAC,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAE/B,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,GAAG,CAAC,KAAK,mCAAmC,EAAE,CAAA;IACxF,CAAC;IAED,8CAA8C;IAC9C,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,kBAAkB,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;IAC1E,CAAC;IAED,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAA;IACvE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AAC3B,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * profile 的 pnpm 构建许可(pnpm-workspace.yaml 的 allowBuilds)计划器(纯函数)。
3
+ *
4
+ * 背景:桥依赖 matrix-bot-sdk,后者硬依赖 @matrix-org/matrix-sdk-crypto-nodejs
5
+ * —— 原生 Rust 加密库,靠 postinstall 下载预编译二进制。pnpm v12 默认不跑依赖
6
+ * 的构建脚本,于是首次 `dsh plugin --profile <p> add dsh-matrix` 会以
7
+ * ERR_PNPM_IGNORED_BUILDS 非零退出:依赖其实已经写进 package.json,但命令失败,
8
+ * dsh 随即中止,profile 的 bundles 也就没挂上 —— 用户看到的现象是「装了却没生效」。
9
+ *
10
+ * pnpm 会把占位行留在 profile 的 pnpm-workspace.yaml 里
11
+ * (`'@matrix-org/matrix-sdk-crypto-nodejs': set this to true or false`),等人改成
12
+ * true。install-profile 在 `dsh plugin add` 之前先把它写对,首次安装一次过;
13
+ * 已经卡住的用户重跑 install-profile 同样能修好。
14
+ *
15
+ * 前置条件:`dsh plugin` 只在 profile 缺 package.json 时才初始化 profile,
16
+ * 所以本模块预建 profile 目录 + pnpm-workspace.yaml 不会被 dsh 覆盖。
17
+ */
18
+ /** 需要构建许可的包(pnpm allowBuilds 的键)。 */
19
+ export declare const CRYPTO_PACKAGE = "@matrix-org/matrix-sdk-crypto-nodejs";
20
+ export interface PnpmWorkspacePlan {
21
+ action: 'create' | 'patch' | 'skip';
22
+ /** 要写入目标文件的完整内容(skip 时无意义)。 */
23
+ text: string;
24
+ reason: string;
25
+ }
26
+ /**
27
+ * 幂等计划:文件不存在(或空)→ create 预建模板;已有该键但值非 true(含 pnpm
28
+ * 留下的占位)→ patch 改成 true;已有 true → skip;无该键但已有 allowBuilds 段
29
+ * → 插进该段;两者都无 → 追加新段。
30
+ */
31
+ export declare function planPnpmBuildAllowance(existing: string | null): PnpmWorkspacePlan;
32
+ //# sourceMappingURL=pnpm-builds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpm-builds.d.ts","sourceRoot":"","sources":["../../src/cli/pnpm-builds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,qCAAqC;AACrC,eAAO,MAAM,cAAc,yCAAyC,CAAA;AAkBpE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;IACnC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,CAqCjF"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * profile 的 pnpm 构建许可(pnpm-workspace.yaml 的 allowBuilds)计划器(纯函数)。
3
+ *
4
+ * 背景:桥依赖 matrix-bot-sdk,后者硬依赖 @matrix-org/matrix-sdk-crypto-nodejs
5
+ * —— 原生 Rust 加密库,靠 postinstall 下载预编译二进制。pnpm v12 默认不跑依赖
6
+ * 的构建脚本,于是首次 `dsh plugin --profile <p> add dsh-matrix` 会以
7
+ * ERR_PNPM_IGNORED_BUILDS 非零退出:依赖其实已经写进 package.json,但命令失败,
8
+ * dsh 随即中止,profile 的 bundles 也就没挂上 —— 用户看到的现象是「装了却没生效」。
9
+ *
10
+ * pnpm 会把占位行留在 profile 的 pnpm-workspace.yaml 里
11
+ * (`'@matrix-org/matrix-sdk-crypto-nodejs': set this to true or false`),等人改成
12
+ * true。install-profile 在 `dsh plugin add` 之前先把它写对,首次安装一次过;
13
+ * 已经卡住的用户重跑 install-profile 同样能修好。
14
+ *
15
+ * 前置条件:`dsh plugin` 只在 profile 缺 package.json 时才初始化 profile,
16
+ * 所以本模块预建 profile 目录 + pnpm-workspace.yaml 不会被 dsh 覆盖。
17
+ */
18
+ /** 需要构建许可的包(pnpm allowBuilds 的键)。 */
19
+ export const CRYPTO_PACKAGE = '@matrix-org/matrix-sdk-crypto-nodejs';
20
+ /** dsh 初始化 profile 时写的 pnpm-workspace.yaml 模板(我们补上 allowBuilds)。 */
21
+ const BASE_TEMPLATE = `packages:
22
+ - .
23
+
24
+ nodeLinker: hoisted
25
+ autoInstallPeers: false
26
+ `;
27
+ function escapeRegExp(text) {
28
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
29
+ }
30
+ const KEY_RE = new RegExp(`^(\\s*)['"]?${escapeRegExp(CRYPTO_PACKAGE)}['"]?\\s*:\\s*(.*)$`);
31
+ const ALLOW_LINE = ` '${CRYPTO_PACKAGE}': true`;
32
+ /**
33
+ * 幂等计划:文件不存在(或空)→ create 预建模板;已有该键但值非 true(含 pnpm
34
+ * 留下的占位)→ patch 改成 true;已有 true → skip;无该键但已有 allowBuilds 段
35
+ * → 插进该段;两者都无 → 追加新段。
36
+ */
37
+ export function planPnpmBuildAllowance(existing) {
38
+ if (existing === null || existing.trim() === '') {
39
+ return {
40
+ action: 'create',
41
+ text: `${BASE_TEMPLATE}allowBuilds:\n${ALLOW_LINE}\n`,
42
+ reason: `已预建 pnpm-workspace.yaml 并放行 ${CRYPTO_PACKAGE} 的构建脚本(否则首次安装会以 ERR_PNPM_IGNORED_BUILDS 失败)`,
43
+ };
44
+ }
45
+ const lines = existing.split('\n');
46
+ for (let i = 0; i < lines.length; i += 1) {
47
+ const matched = KEY_RE.exec(lines[i] ?? '');
48
+ if (matched === null)
49
+ continue;
50
+ const current = (matched[2] ?? '').trim();
51
+ if (current === 'true') {
52
+ return { action: 'skip', text: existing, reason: `${CRYPTO_PACKAGE} 的构建脚本已放行,未改动` };
53
+ }
54
+ lines[i] = `${matched[1]}'${CRYPTO_PACKAGE}': true`;
55
+ return {
56
+ action: 'patch',
57
+ text: lines.join('\n'),
58
+ reason: `已把 ${CRYPTO_PACKAGE} 的构建许可改为 true(原值「${current === '' ? '空' : current}」会挡住首次安装)`,
59
+ };
60
+ }
61
+ const allowIndex = lines.findIndex((line) => /^allowBuilds\s*:\s*$/.test(line));
62
+ if (allowIndex !== -1) {
63
+ lines.splice(allowIndex + 1, 0, ALLOW_LINE);
64
+ return { action: 'patch', text: lines.join('\n'), reason: `已在现有 allowBuilds 段补上 ${CRYPTO_PACKAGE}: true` };
65
+ }
66
+ const base = existing.endsWith('\n') ? existing : `${existing}\n`;
67
+ return {
68
+ action: 'patch',
69
+ text: `${base}allowBuilds:\n${ALLOW_LINE}\n`,
70
+ reason: `已新增 allowBuilds 段放行 ${CRYPTO_PACKAGE}`,
71
+ };
72
+ }
73
+ //# sourceMappingURL=pnpm-builds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpm-builds.js","sourceRoot":"","sources":["../../src/cli/pnpm-builds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,qCAAqC;AACrC,MAAM,CAAC,MAAM,cAAc,GAAG,sCAAsC,CAAA;AAEpE,oEAAoE;AACpE,MAAM,aAAa,GAAG;;;;;CAKrB,CAAA;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;AACpD,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,eAAe,YAAY,CAAC,cAAc,CAAC,qBAAqB,CAAC,CAAA;AAE3F,MAAM,UAAU,GAAG,MAAM,cAAc,SAAS,CAAA;AAShD;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAuB;IAC5D,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAChD,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,GAAG,aAAa,iBAAiB,UAAU,IAAI;YACrD,MAAM,EAAE,+BAA+B,cAAc,6CAA6C;SACnG,CAAA;IACH,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QAC3C,IAAI,OAAO,KAAK,IAAI;YAAE,SAAQ;QAC9B,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACzC,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;YACvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,cAAc,eAAe,EAAE,CAAA;QACrF,CAAC;QACD,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,cAAc,SAAS,CAAA;QACnD,OAAO;YACL,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YACtB,MAAM,EAAE,MAAM,cAAc,oBAAoB,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,WAAW;SAC1F,CAAA;IACH,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC/E,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,MAAM,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC,CAAA;QAC3C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,wBAAwB,cAAc,QAAQ,EAAE,CAAA;IAC5G,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,CAAA;IACjE,OAAO;QACL,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,GAAG,IAAI,iBAAiB,UAAU,IAAI;QAC5C,MAAM,EAAE,uBAAuB,cAAc,EAAE;KAChD,CAAA;AACH,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * profile 用户层 cordis.patch.yml 的 matrix-bridge 覆盖块生成(纯函数)。
3
+ *
4
+ * 桥插件以 bundle 方式挂进 profile 后,凭据/偏好由 profile 自己的
5
+ * cordis.patch.yml 按行 id `matrix-bridge` 覆盖 config(整 config 替换语义,
6
+ * spike 实测,见 docs/dsh-matrix-port-feasibility.md §9)。install-profile
7
+ * 用本模块把 ~/.dsh/dsh-matrix.json 变成那段 YAML。
8
+ *
9
+ * 所有标量用 JSON 字符串形式写出(YAML 双引号标量与其兼容),避免密码/URL
10
+ * 里的特殊字符破坏 YAML。
11
+ */
12
+ import type { OpsConfig } from './config-store.ts';
13
+ /** 生成 `- id: matrix-bridge …` 覆盖块(不含既有内容)。 */
14
+ export declare function renderBridgePatch(cfg: OpsConfig): string;
15
+ export interface ProfilePatchPlan {
16
+ action: 'create' | 'append' | 'skip';
17
+ /** 要写入目标文件的完整内容(skip 时无意义)。 */
18
+ text: string;
19
+ reason: string;
20
+ }
21
+ /**
22
+ * 幂等计划:文件不存在/为空/只剩 `[]` 占位→create;已有 matrix-bridge 行→skip
23
+ * (绝不覆盖用户手改的 profile);存在真实内容但无该行→append。
24
+ */
25
+ export declare function planProfilePatch(existing: string | null, cfg: OpsConfig): ProfilePatchPlan;
26
+ //# sourceMappingURL=profile-patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-patch.d.ts","sourceRoot":"","sources":["../../src/cli/profile-patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AASlD,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAUxD;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;IACpC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAoBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,SAAS,GAAG,gBAAgB,CAa1F"}
@@ -0,0 +1,56 @@
1
+ const HEADER = '# 由 dsh-matrix install-profile 生成:桥凭据与偏好(整 config 替换语义)。';
2
+ const ROW_ID = 'matrix-bridge';
3
+ function yamlString(value) {
4
+ return JSON.stringify(value);
5
+ }
6
+ /** 生成 `- id: matrix-bridge …` 覆盖块(不含既有内容)。 */
7
+ export function renderBridgePatch(cfg) {
8
+ const lines = [`- id: ${ROW_ID}`, ' config:'];
9
+ lines.push(` homeserver: ${yamlString(cfg.homeserver)}`);
10
+ if (cfg.accessToken)
11
+ lines.push(` accessToken: ${yamlString(cfg.accessToken)}`);
12
+ if (cfg.username)
13
+ lines.push(` username: ${yamlString(cfg.username)}`);
14
+ if (cfg.password)
15
+ lines.push(` password: ${yamlString(cfg.password)}`);
16
+ lines.push(` trustedUsers: [${cfg.trustedUsers.map(yamlString).join(', ')}]`);
17
+ lines.push(` workdir: ${yamlString(cfg.workdir)}`);
18
+ lines.push(` logLevel: ${yamlString(cfg.logLevel ?? 'info')}`);
19
+ return `${HEADER}\n${lines.join('\n')}\n`;
20
+ }
21
+ /**
22
+ * 文件里除了注释和空行,是否只剩一个空数组占位 `[]`。
23
+ *
24
+ * dsh 初始化 profile 时写的模板正是「三段注释 + 一行 `[]`」(见 dsh-app-boot 的
25
+ * PROFILE_PATCH_TEMPLATE)。它**不是空文件**,语义上却等于"没有用户层配置"。
26
+ * 早先只判断 `trim() === ''`,于是模板会掉进 append 分支,产出
27
+ * `[]` 后面再跟 `- id: …` —— 那是**非法 YAML**(一个文档不能既结束于 flow
28
+ * sequence 又接着 block sequence),profile 直接解析失败、桥起不来。
29
+ */
30
+ function isPlaceholderOnly(text) {
31
+ const meaningful = text
32
+ .split('\n')
33
+ .filter((line) => line.trim() !== '' && !line.trim().startsWith('#'))
34
+ .join('\n')
35
+ .trim();
36
+ return meaningful === '' || meaningful === '[]';
37
+ }
38
+ /**
39
+ * 幂等计划:文件不存在/为空/只剩 `[]` 占位→create;已有 matrix-bridge 行→skip
40
+ * (绝不覆盖用户手改的 profile);存在真实内容但无该行→append。
41
+ */
42
+ export function planProfilePatch(existing, cfg) {
43
+ const block = renderBridgePatch(cfg);
44
+ if (existing === null || existing.trim() === '') {
45
+ return { action: 'create', text: block, reason: existing === null ? 'profile 尚无 cordis.patch.yml,已生成' : 'cordis.patch.yml 为空,已写入覆盖块' };
46
+ }
47
+ if (/-\s+id:\s*matrix-bridge/.test(existing) || /^id:\s*matrix-bridge/m.test(existing)) {
48
+ return { action: 'skip', text: '', reason: 'profile 的 cordis.patch.yml 已含 matrix-bridge 覆盖行,未改动(尊重用户编辑)' };
49
+ }
50
+ if (isPlaceholderOnly(existing)) {
51
+ return { action: 'create', text: block, reason: 'cordis.patch.yml 只有 dsh 的空数组占位([]),已整份替换为覆盖块(直接追加会产出非法 YAML)' };
52
+ }
53
+ const base = existing.endsWith('\n') ? existing : `${existing}\n`;
54
+ return { action: 'append', text: `${base}\n${block}`, reason: '已把 matrix-bridge 覆盖行追加到现有 cordis.patch.yml 末尾' };
55
+ }
56
+ //# sourceMappingURL=profile-patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-patch.js","sourceRoot":"","sources":["../../src/cli/profile-patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { OpsConfig } from './config-store.ts';
2
+ export interface SetupFlags {
3
+ yes?: boolean;
4
+ homeserver?: string;
5
+ username?: string;
6
+ password?: string;
7
+ token?: string;
8
+ trusted?: string;
9
+ workdir?: string;
10
+ profile?: string;
11
+ logLevel?: string;
12
+ }
13
+ /** 交互问输入器(可注入 fake 供测试)。 */
14
+ export type Ask = (prompt: string, opts?: {
15
+ silent?: boolean;
16
+ }) => Promise<string>;
17
+ export interface SetupDeps {
18
+ homedir: string;
19
+ ask: Ask;
20
+ log: (line: string) => void;
21
+ close: () => void;
22
+ }
23
+ export declare function defaultProfileName(): string;
24
+ export declare function defaultWorkdir(homedir: string): string;
25
+ /** 校验 + 组装 flag 配置(非交互路径,纯函数,可单测)。 */
26
+ export declare function configFromFlags(flags: SetupFlags, homedir: string): OpsConfig | {
27
+ error: string;
28
+ };
29
+ /**
30
+ * TTY 逐行问输入器;silent 用不回显(星号)模式。
31
+ * 移植自 pi-courier src/setup.ts createPrompter 的 TTY 分支。
32
+ */
33
+ export declare function createTTYAsk(): {
34
+ ask: Ask;
35
+ close: () => void;
36
+ };
37
+ /** 向导入口:--yes 走 flag;否则 TTY 交互。返回 0/1 退出码。 */
38
+ export declare function runSetup(flags: SetupFlags, deps?: Partial<SetupDeps>): Promise<number>;
39
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,mBAAmB,CAAA;AAG/D,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,4BAA4B;AAC5B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAElF,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,GAAG,CAAA;IACR,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAMD,sCAAsC;AACtC,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAmCjG;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,MAAM,IAAI,CAAA;CAAE,CA4C9D;AA0FD,8CAA8C;AAC9C,wBAAsB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAwB5F"}
@@ -0,0 +1,234 @@
1
+ /**
2
+ * setup 向导:交互式或非交互(--yes + 全 flag)生成 ~/.dsh/dsh-matrix.json。
3
+ *
4
+ * 交互器移植自 pi-courier src/setup.ts 的 createPrompter(TTY readline +
5
+ * 静默密码星号回显,MIT),问项按 DSH 语义收缩:homeserver → 登录方式
6
+ * (1 密码 / 2 token)→ 信任用户 → 工作目录 → 实例名(= dsh profile 名)。
7
+ * 写的是运维配置文件,profile 挂载由 install-profile/指引完成。
8
+ */
9
+ import { stdin, stdout } from 'node:process';
10
+ import * as os from 'node:os';
11
+ import * as path from 'node:path';
12
+ import { createInterface } from 'node:readline';
13
+ import { loadOpsConfig, saveOpsConfig } from "./config-store.js";
14
+ export function defaultProfileName() {
15
+ return 'matrix';
16
+ }
17
+ export function defaultWorkdir(homedir) {
18
+ return path.join(homedir, 'dsh-matrix-work');
19
+ }
20
+ function isOpsLevel(v) {
21
+ return v === 'debug' || v === 'info' || v === 'warn' || v === 'error';
22
+ }
23
+ /** 校验 + 组装 flag 配置(非交互路径,纯函数,可单测)。 */
24
+ export function configFromFlags(flags, homedir) {
25
+ const missing = [];
26
+ if (!flags.homeserver)
27
+ missing.push('--homeserver');
28
+ const hasPassword = Boolean(flags.username && flags.password);
29
+ const hasToken = Boolean(flags.token);
30
+ if (!hasPassword && !hasToken)
31
+ missing.push('--token 或 --username+--password');
32
+ if (!flags.trusted)
33
+ missing.push('--trusted');
34
+ if (missing.length > 0) {
35
+ return { error: `--yes 非交互模式需要补齐: ${missing.join(' ')}` };
36
+ }
37
+ if (flags.logLevel && !isOpsLevel(flags.logLevel)) {
38
+ return { error: `--log-level 非法: ${flags.logLevel}(可选 debug / info / warn / error)` };
39
+ }
40
+ const trustedUsers = (flags.trusted ?? '')
41
+ .split(',')
42
+ .map((s) => s.trim())
43
+ .filter(Boolean);
44
+ if (trustedUsers.length === 0)
45
+ return { error: '--trusted 不能为空' };
46
+ const bad = trustedUsers.filter((u) => !u.startsWith('@'));
47
+ if (bad.length > 0) {
48
+ return { error: `信任用户 MXID 应以 @ 开头(如 @barry:example.com),收到: ${bad.join(', ')}` };
49
+ }
50
+ const cfg = {
51
+ profile: flags.profile ?? defaultProfileName(),
52
+ homeserver: String(flags.homeserver),
53
+ trustedUsers,
54
+ workdir: flags.workdir ?? defaultWorkdir(homedir),
55
+ };
56
+ if (hasToken)
57
+ cfg.accessToken = String(flags.token);
58
+ else {
59
+ cfg.username = String(flags.username);
60
+ cfg.password = String(flags.password);
61
+ }
62
+ if (flags.logLevel)
63
+ cfg.logLevel = flags.logLevel;
64
+ return cfg;
65
+ }
66
+ /**
67
+ * TTY 逐行问输入器;silent 用不回显(星号)模式。
68
+ * 移植自 pi-courier src/setup.ts createPrompter 的 TTY 分支。
69
+ */
70
+ export function createTTYAsk() {
71
+ const rl = createInterface({ input: stdin, output: stdout });
72
+ // 每次 ask 都注册一个 once close 监听,向导问十余次会触默认 10 上限告警,放开。
73
+ rl.setMaxListeners(0);
74
+ return {
75
+ ask: (prompt, opts) => new Promise((resolve) => {
76
+ let done = false;
77
+ const finish = (answer) => {
78
+ if (!done) {
79
+ done = true;
80
+ resolve(answer);
81
+ }
82
+ };
83
+ if (opts?.silent) {
84
+ // Node 文档化的 _writeToOutput 遮写法:提示语放行,输入渲染为 *。
85
+ const output = rl;
86
+ const origWrite = output._writeToOutput;
87
+ let stars = 0;
88
+ output._writeToOutput = (str) => {
89
+ if (str === prompt)
90
+ origWrite.call(rl, prompt);
91
+ else if (str === '\x7f' || str === '\b') {
92
+ if (stars > 0) {
93
+ origWrite.call(rl, '\b \b');
94
+ stars--;
95
+ }
96
+ }
97
+ else if (/^[\x20-\x7e\u00a0-\uffff]$/.test(str)) {
98
+ origWrite.call(rl, '*');
99
+ stars++;
100
+ }
101
+ // 其余控制序列吞掉(不回显、不动光标)
102
+ };
103
+ rl.question(prompt, (answer) => {
104
+ output._writeToOutput = origWrite;
105
+ origWrite.call(rl, '\n');
106
+ finish(answer);
107
+ });
108
+ }
109
+ else {
110
+ rl.question(prompt, finish);
111
+ }
112
+ rl.once('close', () => finish(''));
113
+ }),
114
+ close: () => rl.close(),
115
+ };
116
+ }
117
+ async function interactiveConfig(deps) {
118
+ const { ask, log } = deps;
119
+ const existing = loadOpsConfig(deps.homedir) ?? undefined;
120
+ const d = (v) => (v ?? '').trim();
121
+ // 1. homeserver
122
+ const hsDefault = existing?.homeserver ?? '';
123
+ const homeserver = d(await ask(hsDefault ? `Matrix homeserver URL [默认 ${hsDefault}]: ` : 'Matrix homeserver URL (如 https://matrix.example.com): ')) || hsDefault;
124
+ if (!homeserver)
125
+ return { error: 'homeserver URL 不能为空' };
126
+ // 2. 登录方式
127
+ const mode = d(await ask('登录方式 [1=用户名密码, 2=粘贴 access token] (1): ')) || '1';
128
+ let username;
129
+ let password;
130
+ let accessToken;
131
+ if (mode === '2') {
132
+ accessToken = d(await ask('粘贴 access token (syt_...): '));
133
+ if (!accessToken)
134
+ return { error: 'token 不能为空' };
135
+ }
136
+ else if (mode === '1') {
137
+ username = d(await ask(`bot 用户名${existing?.username ? ` [默认 ${existing.username}]` : ''}: `)) || (existing?.username ?? '');
138
+ if (!username)
139
+ return { error: '用户名不能为空' };
140
+ password = (await ask('bot 密码(不回显): ', { silent: true })).trim() || (existing?.password ?? '');
141
+ if (!password)
142
+ return { error: '密码不能为空' };
143
+ }
144
+ else {
145
+ return { error: `未知登录方式: ${mode}(只支持 1 或 2)` };
146
+ }
147
+ // 3. 信任用户
148
+ const trustedDefault = existing?.trustedUsers?.join(', ') ?? '';
149
+ const trustedRaw = d(await ask(`信任用户(管理员)MXID${trustedDefault ? ` [默认 ${trustedDefault}]` : ''},逗号分隔: `)) || trustedDefault;
150
+ if (!trustedRaw)
151
+ return { error: '至少要有一个信任用户' };
152
+ const trustedUsers = trustedRaw.split(',').map((s) => s.trim()).filter(Boolean);
153
+ const bad = trustedUsers.filter((u) => !u.startsWith('@'));
154
+ if (bad.length > 0)
155
+ return { error: `MXID 应以 @ 开头(如 @barry:example.com),收到: ${bad.join(', ')}` };
156
+ // 4. 工作目录
157
+ const workdirDefault = existing?.workdir ?? defaultWorkdir(deps.homedir);
158
+ const workdir = d(await ask(`DSH agent 工作目录 [默认 ${workdirDefault}]: `)) || workdirDefault;
159
+ // 5. 实例名(= dsh profile 名)
160
+ const profileDefault = existing?.profile ?? defaultProfileName();
161
+ const profile = d(await ask(`实例名(dsh profile 名,多台部署用来区分)[默认 ${profileDefault}]: `)) || profileDefault;
162
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(profile)) {
163
+ return { error: `实例名需为小写字母/数字/._- 且以字母数字开头,收到: ${profile}` };
164
+ }
165
+ log('');
166
+ return {
167
+ profile,
168
+ homeserver,
169
+ trustedUsers,
170
+ workdir,
171
+ ...(accessToken ? { accessToken } : { username, password }),
172
+ ...(existing?.logLevel ? { logLevel: existing.logLevel } : {}),
173
+ };
174
+ }
175
+ /** setup 收尾打印:profile 挂载指引 + 后续命令。 */
176
+ function printNextSteps(cfg, file, log) {
177
+ const loginDesc = cfg.accessToken ? 'token 登录' : `密码登录(${cfg.username})`;
178
+ log(`✅ 运维配置已写入 ${file}(权限 0600)`);
179
+ log(` homeserver: ${cfg.homeserver}(${loginDesc})`);
180
+ log(` 信任用户: ${cfg.trustedUsers.join(', ')}`);
181
+ log(` 工作目录: ${cfg.workdir}`);
182
+ log(` 实例名: ${cfg.profile}(对应 dsh profile ~/.dsh/profiles/${cfg.profile})`);
183
+ log('');
184
+ log('下一步(二选一):');
185
+ log(' A. 一键挂载: dsh-matrix install-profile');
186
+ log(` (等价手动三步: dsh plugin --profile ${cfg.profile} add <本包>` + ',写 profile patch,再启动)');
187
+ log(` B. 手动: 1) dsh plugin --profile ${cfg.profile} add dsh-matrix`);
188
+ log(' 2) 编辑 ~/.dsh/profiles/' + cfg.profile + '/cordis.patch.yml,加入:');
189
+ const block = [
190
+ ` - id: matrix-bridge`,
191
+ ` config:`,
192
+ ` homeserver: ${JSON.stringify(cfg.homeserver)}`,
193
+ ];
194
+ if (cfg.accessToken)
195
+ block.push(` accessToken: ${JSON.stringify(cfg.accessToken)}`);
196
+ else {
197
+ block.push(` username: ${JSON.stringify(cfg.username)}`);
198
+ block.push(` password: ${JSON.stringify(cfg.password)}`);
199
+ }
200
+ block.push(` trustedUsers: [${cfg.trustedUsers.map((u) => JSON.stringify(u)).join(', ')}]`);
201
+ block.push(` workdir: ${JSON.stringify(cfg.workdir)}`);
202
+ block.push(` logLevel: ${JSON.stringify(cfg.logLevel ?? 'info')}`);
203
+ for (const l of block)
204
+ log(l);
205
+ log(` 然后: dsh-matrix run(前台试跑)或 dsh-matrix enable(systemd 自启)`);
206
+ }
207
+ /** 向导入口:--yes 走 flag;否则 TTY 交互。返回 0/1 退出码。 */
208
+ export async function runSetup(flags, deps) {
209
+ const homedir = deps?.homedir ?? os.homedir();
210
+ let cfg;
211
+ let close = () => { };
212
+ const log = deps?.log ?? ((l) => console.log(l));
213
+ if (flags.yes) {
214
+ cfg = configFromFlags(flags, homedir);
215
+ }
216
+ else {
217
+ const tty = deps?.ask ? { ask: deps.ask, close: () => { } } : createTTYAsk();
218
+ close = tty.close;
219
+ cfg = await interactiveConfig({ homedir, ask: tty.ask, log, close });
220
+ }
221
+ try {
222
+ if ('error' in cfg) {
223
+ log(`❌ ${cfg.error}`);
224
+ return 1;
225
+ }
226
+ const file = saveOpsConfig(cfg, homedir);
227
+ printNextSteps(cfg, file, log);
228
+ return 0;
229
+ }
230
+ finally {
231
+ close(); // 释放 stdin 监听,让进程自然退出
232
+ }
233
+ }
234
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface BusFailureHintInput {
2
+ euid: number;
3
+ xdgRuntimeDir?: string;
4
+ xdgOwnerUid?: number | null;
5
+ stderr?: string;
6
+ }
7
+ /** 纯判断:这次 systemctl 失败是不是"总线属主错位"陷阱;不是则返回 null。 */
8
+ export declare function busFailureHint(input: BusFailureHintInput): string | null;
9
+ /** 目录属主 uid;无法解析(不存在/不可达)返回 null。 */
10
+ export declare function dirOwnerUid(dir: string | undefined): number | null;
11
+ //# sourceMappingURL=systemd-hint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systemd-hint.d.ts","sourceRoot":"","sources":["../../src/cli/systemd-hint.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CA6BxE;AAED,qCAAqC;AACrC,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAOlE"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `su <用户>`(不带 -)导致的 systemctl --user 总线错配提示。
3
+ * 整文件移植自 pi-courier src/systemd-hint.ts(MIT,issue #59),仅改注释语言。
4
+ */
5
+ import * as fs from 'node:fs';
6
+ /** 纯判断:这次 systemctl 失败是不是"总线属主错位"陷阱;不是则返回 null。 */
7
+ export function busFailureHint(input) {
8
+ const { euid, xdgRuntimeDir, xdgOwnerUid, stderr } = input;
9
+ const stderrText = typeof stderr === 'string' ? stderr : undefined;
10
+ const stderrPointsAtBus = !!stderrText &&
11
+ (/connect to bus of other user/i.test(stderrText) ||
12
+ (/user scope bus/i.test(stderrText) && /not permitted/i.test(stderrText)));
13
+ const ownerMismatchWithEperm = typeof xdgOwnerUid === 'number' &&
14
+ xdgOwnerUid !== euid &&
15
+ !!stderrText &&
16
+ /not permitted|EPERM/i.test(stderrText);
17
+ if (!stderrPointsAtBus && !ownerMismatchWithEperm)
18
+ return null;
19
+ const tail = [
20
+ ' 常见原因:用 `su <用户>`(不带 -)切换用户时继承了原用户的环境,或当前 shell 缺少完整登录会话。',
21
+ ' 解决:export XDG_RUNTIME_DIR=/run/user/$(id -u) 后重试,或改用 `su - <用户>` 重新切换;',
22
+ ' 若 /run/user/$(id -u) 不存在,请以该用户登录一次,或由 root 执行 loginctl enable-linger <用户>。',
23
+ ];
24
+ if (typeof xdgOwnerUid === 'number' && xdgOwnerUid !== euid) {
25
+ return [
26
+ `💡 检测到 XDG_RUNTIME_DIR=${xdgRuntimeDir ?? '(未设置)'}(属主 uid ${xdgOwnerUid})指向其他用户的会话总线,而当前用户是 uid ${euid}。`,
27
+ ...tail,
28
+ ].join('\n');
29
+ }
30
+ return [
31
+ `💡 未能连接到当前用户的 systemd 会话总线(XDG_RUNTIME_DIR=${xdgRuntimeDir ?? '未设置'})。`,
32
+ ...tail,
33
+ ].join('\n');
34
+ }
35
+ /** 目录属主 uid;无法解析(不存在/不可达)返回 null。 */
36
+ export function dirOwnerUid(dir) {
37
+ if (!dir)
38
+ return null;
39
+ try {
40
+ return fs.statSync(dir).uid;
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ //# sourceMappingURL=systemd-hint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systemd-hint.js","sourceRoot":"","sources":["../../src/cli/systemd-hint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAS7B,mDAAmD;AACnD,MAAM,UAAU,cAAc,CAAC,KAA0B;IACvD,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;IAC1D,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;IAClE,MAAM,iBAAiB,GACrB,CAAC,CAAC,UAAU;QACZ,CAAC,+BAA+B,CAAC,IAAI,CAAC,UAAU,CAAC;YAC/C,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;IAC9E,MAAM,sBAAsB,GAC1B,OAAO,WAAW,KAAK,QAAQ;QAC/B,WAAW,KAAK,IAAI;QACpB,CAAC,CAAC,UAAU;QACZ,sBAAsB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACzC,IAAI,CAAC,iBAAiB,IAAI,CAAC,sBAAsB;QAAE,OAAO,IAAI,CAAA;IAE9D,MAAM,IAAI,GAAG;QACX,6DAA6D;QAC7D,2EAA2E;QAC3E,+EAA+E;KAChF,CAAA;IACD,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QAC5D,OAAO;YACL,0BAA0B,aAAa,IAAI,OAAO,WAAW,WAAW,2BAA2B,IAAI,GAAG;YAC1G,GAAG,IAAI;SACR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACd,CAAC;IACD,OAAO;QACL,8CAA8C,aAAa,IAAI,KAAK,IAAI;QACxE,GAAG,IAAI;KACR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,WAAW,CAAC,GAAuB;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,CAAA;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}