dsh-archify-projects 0.0.0-stage → 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 (148) hide show
  1. package/README.md +204 -2
  2. package/cordis.patch.yml +99 -0
  3. package/dist/client.js +2261 -0
  4. package/dist/client.js.map +1 -0
  5. package/docs/CONTAINER.md +197 -0
  6. package/docs/DESIGN.md +905 -0
  7. package/docs/HANDOVER.md +391 -0
  8. package/docs/PUBLISH.md +107 -0
  9. package/icon.svg +12 -0
  10. package/lib/host/archify-skill.js +116 -0
  11. package/lib/host/archify-skill.js.map +1 -0
  12. package/lib/host/config.js +95 -0
  13. package/lib/host/config.js.map +1 -0
  14. package/lib/host/context.js +28 -0
  15. package/lib/host/context.js.map +1 -0
  16. package/lib/host/git.js +185 -0
  17. package/lib/host/git.js.map +1 -0
  18. package/lib/host/http.js +184 -0
  19. package/lib/host/http.js.map +1 -0
  20. package/lib/host/index.js +90 -0
  21. package/lib/host/index.js.map +1 -0
  22. package/lib/host/policy.js +124 -0
  23. package/lib/host/policy.js.map +1 -0
  24. package/lib/host/routes.js +667 -0
  25. package/lib/host/routes.js.map +1 -0
  26. package/lib/host/runner.js +913 -0
  27. package/lib/host/runner.js.map +1 -0
  28. package/lib/host/store.js +370 -0
  29. package/lib/host/store.js.map +1 -0
  30. package/lib/host/workspace.js +65 -0
  31. package/lib/host/workspace.js.map +1 -0
  32. package/lib/shared/format.js +138 -0
  33. package/lib/shared/format.js.map +1 -0
  34. package/lib/shared/prompt.js +257 -0
  35. package/lib/shared/prompt.js.map +1 -0
  36. package/lib/shared/protocol.js +31 -0
  37. package/lib/shared/protocol.js.map +1 -0
  38. package/lib/shared/types.js +173 -0
  39. package/lib/shared/types.js.map +1 -0
  40. package/locale/en.json +6 -0
  41. package/locale/zh.json +6 -0
  42. package/package.json +98 -4
  43. package/skills/archify/LICENSE +22 -0
  44. package/skills/archify/SKILL.md +109 -0
  45. package/skills/archify/THIRD_PARTY_NOTICES.md +69 -0
  46. package/skills/archify/assets/JetBrainsMono-OFL.txt +93 -0
  47. package/skills/archify/assets/template.html +13709 -0
  48. package/skills/archify/bin/archify.mjs +6936 -0
  49. package/skills/archify/bin/delivery-update.mjs +91 -0
  50. package/skills/archify/bin/finalize.mjs +895 -0
  51. package/skills/archify/bin/open-artifact.mjs +146 -0
  52. package/skills/archify/bin/preview.mjs +969 -0
  53. package/skills/archify/bin/recover-output.mjs +41 -0
  54. package/skills/archify/bin/visual-check.mjs +2667 -0
  55. package/skills/archify/brand-marks/README.md +31 -0
  56. package/skills/archify/brand-marks/catalog.json +131 -0
  57. package/skills/archify/delta/architecture-delta.mjs +1313 -0
  58. package/skills/archify/examples/agent-run.lifecycle.json +55 -0
  59. package/skills/archify/examples/agent-tool-call.workflow.json +74 -0
  60. package/skills/archify/examples/async-job-roundtrip.sequence.json +56 -0
  61. package/skills/archify/examples/brand-aware-delivery.architecture.json +34 -0
  62. package/skills/archify/examples/cache-miss-request.sequence.json +77 -0
  63. package/skills/archify/examples/checkout-platform.base.architecture.json +32 -0
  64. package/skills/archify/examples/checkout-platform.head.architecture.json +32 -0
  65. package/skills/archify/examples/dataflow-product-analytics.html +13820 -0
  66. package/skills/archify/examples/deployment-release.lifecycle.json +48 -0
  67. package/skills/archify/examples/event-stream.dataflow.json +52 -0
  68. package/skills/archify/examples/holiday-planning.workflow.json +143 -0
  69. package/skills/archify/examples/incident-response.workflow.json +59 -0
  70. package/skills/archify/examples/lifecycle-agent-run.html +13765 -0
  71. package/skills/archify/examples/locales/de.json +425 -0
  72. package/skills/archify/examples/locales/es.json +440 -0
  73. package/skills/archify/examples/locales/fr.json +425 -0
  74. package/skills/archify/examples/locales/fr.partial.json +22 -0
  75. package/skills/archify/examples/locales/it.json +425 -0
  76. package/skills/archify/examples/locales/ja.json +425 -0
  77. package/skills/archify/examples/locales/ko.json +440 -0
  78. package/skills/archify/examples/locales/pt.json +425 -0
  79. package/skills/archify/examples/locales/ru.json +425 -0
  80. package/skills/archify/examples/product-analytics.dataflow.json +71 -0
  81. package/skills/archify/examples/production-deployment.architecture.json +51 -0
  82. package/skills/archify/examples/release-delivery.workflow.json +57 -0
  83. package/skills/archify/examples/sequence-cache-miss-request.html +13835 -0
  84. package/skills/archify/examples/web-app-rendered.html +13808 -0
  85. package/skills/archify/examples/web-app.architecture.json +41 -0
  86. package/skills/archify/examples/workflow-agent-tool-call-rendered.html +13826 -0
  87. package/skills/archify/migrations/workflow-v2.mjs +280 -0
  88. package/skills/archify/package.json +17 -0
  89. package/skills/archify/recipes/scenarios.mjs +430 -0
  90. package/skills/archify/references/architecture-layout-repair.md +18 -0
  91. package/skills/archify/references/authoring-contract.md +419 -0
  92. package/skills/archify/references/authoring-defaults.md +41 -0
  93. package/skills/archify/references/brand-marks.md +82 -0
  94. package/skills/archify/references/delivery-contract.md +588 -0
  95. package/skills/archify/references/repository-authoring.md +105 -0
  96. package/skills/archify/references/update-awareness.md +12 -0
  97. package/skills/archify/references/viewer-runtime.md +35 -0
  98. package/skills/archify/renderers/architecture/grid.mjs +62 -0
  99. package/skills/archify/renderers/architecture/labels.mjs +215 -0
  100. package/skills/archify/renderers/architecture/render-architecture.mjs +1001 -0
  101. package/skills/archify/renderers/architecture/routing.mjs +1157 -0
  102. package/skills/archify/renderers/dataflow/README.md +105 -0
  103. package/skills/archify/renderers/dataflow/render-dataflow.mjs +544 -0
  104. package/skills/archify/renderers/lifecycle/README.md +171 -0
  105. package/skills/archify/renderers/lifecycle/grid-routing.mjs +429 -0
  106. package/skills/archify/renderers/lifecycle/render-lifecycle.mjs +938 -0
  107. package/skills/archify/renderers/sequence/README.md +129 -0
  108. package/skills/archify/renderers/sequence/render-sequence.mjs +526 -0
  109. package/skills/archify/renderers/shared/atomic-output.mjs +1982 -0
  110. package/skills/archify/renderers/shared/brand-marks.mjs +670 -0
  111. package/skills/archify/renderers/shared/cli.mjs +451 -0
  112. package/skills/archify/renderers/shared/desktop-readability.mjs +132 -0
  113. package/skills/archify/renderers/shared/diagnostics.mjs +173 -0
  114. package/skills/archify/renderers/shared/engineering-profiles.mjs +157 -0
  115. package/skills/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
  116. package/skills/archify/renderers/shared/generated-validators.mjs +134 -0
  117. package/skills/archify/renderers/shared/geometry.mjs +1941 -0
  118. package/skills/archify/renderers/shared/i18n.mjs +603 -0
  119. package/skills/archify/renderers/shared/layout-report.mjs +42 -0
  120. package/skills/archify/renderers/shared/legend.mjs +217 -0
  121. package/skills/archify/renderers/shared/output-path.mjs +440 -0
  122. package/skills/archify/renderers/shared/path-semantics.mjs +1017 -0
  123. package/skills/archify/renderers/shared/portable-path.mjs +408 -0
  124. package/skills/archify/renderers/shared/repository-evidence.mjs +352 -0
  125. package/skills/archify/renderers/shared/repository-location.mjs +59 -0
  126. package/skills/archify/renderers/shared/route-quality.mjs +614 -0
  127. package/skills/archify/renderers/shared/sidecar-path.mjs +38 -0
  128. package/skills/archify/renderers/shared/spatial-grid.mjs +66 -0
  129. package/skills/archify/renderers/shared/text-fit.mjs +105 -0
  130. package/skills/archify/renderers/shared/utils.mjs +254 -0
  131. package/skills/archify/renderers/shared/validator.mjs +86 -0
  132. package/skills/archify/renderers/workflow/README.md +301 -0
  133. package/skills/archify/renderers/workflow/render-workflow.mjs +37 -0
  134. package/skills/archify/renderers/workflow/workflow-compiler.mjs +4913 -0
  135. package/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  136. package/skills/archify/schemas/README.md +256 -0
  137. package/skills/archify/schemas/architecture.schema.json +152 -0
  138. package/skills/archify/schemas/common.schema.json +181 -0
  139. package/skills/archify/schemas/dataflow.schema.json +254 -0
  140. package/skills/archify/schemas/lifecycle.schema.json +280 -0
  141. package/skills/archify/schemas/sequence.schema.json +234 -0
  142. package/skills/archify/schemas/workflow.schema.json +439 -0
  143. package/skills/archify/scripts/check-render-output.mjs +1388 -0
  144. package/skills/archify/scripts/check-update.mjs +1710 -0
  145. package/skills/archify/scripts/delivery-update-child.mjs +23 -0
  146. package/skills/archify/scripts/render-examples.mjs +26 -0
  147. package/skills/archify/scripts/update-contract.mjs +182 -0
  148. package/skills/archify/skill-release.json +10 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * 插件配置:默认值、解析与校验。
3
+ *
4
+ * 不用 schemastery 是因为本插件只有 5 个标量字段,而 schemastery 会引入一个
5
+ * 宿主内部包的硬依赖;对第三方插件来说,手写解析 + 明确报错更稳。
6
+ */
7
+ import os from 'node:os';
8
+ import path from 'node:path';
9
+ /** DSH 配置根。 */
10
+ export function dshHome() {
11
+ const env = process.env.DSH_HOME;
12
+ if (typeof env === 'string' && env.trim().length > 0)
13
+ return path.resolve(env.trim());
14
+ return path.join(os.homedir(), '.dsh');
15
+ }
16
+ /**
17
+ * 解析配置。
18
+ *
19
+ * 非法值一律回落到默认值并**记录**(由调用方打印),不抛异常 ——
20
+ * 一个配置项写错不应该让整个插件在宿主启动期炸掉。
21
+ */
22
+ export function resolveConfig(raw, warnings) {
23
+ const dataDirDefault = path.join(dshHome(), 'dsh-archify-projects');
24
+ const dataDir = readString(raw?.dataDir, dataDirDefault, 'dataDir', warnings);
25
+ const presetId = readString(raw?.presetId, 'archify-projects', 'presetId', warnings);
26
+ const askPresetId = readString(raw?.askPresetId, 'archify-projects-ask', 'askPresetId', warnings);
27
+ const skillRaw = raw?.archifySkillDir;
28
+ const archifySkillDir = typeof skillRaw === 'string' && skillRaw.trim().length > 0
29
+ ? path.resolve(skillRaw.trim())
30
+ : null;
31
+ return {
32
+ dataDir: path.resolve(dataDir),
33
+ presetId,
34
+ askPresetId,
35
+ archifySkillDir,
36
+ runTimeoutMs: readPositiveInt(raw?.runTimeoutMs, 20 * 60 * 1000, 'runTimeoutMs', warnings),
37
+ askTimeoutMs: readPositiveInt(raw?.askTimeoutMs, 6 * 60 * 1000, 'askTimeoutMs', warnings),
38
+ keepRuns: readPositiveInt(raw?.keepRuns, 20, 'keepRuns', warnings),
39
+ pricing: readPricing(raw?.pricing, warnings),
40
+ };
41
+ }
42
+ function readString(value, fallback, field, warnings) {
43
+ if (value === undefined || value === null)
44
+ return fallback;
45
+ if (typeof value !== 'string' || value.trim().length === 0) {
46
+ warnings.push(`配置项 ${field} 不是非空字符串,已回落到默认值`);
47
+ return fallback;
48
+ }
49
+ return value.trim();
50
+ }
51
+ function readPositiveInt(value, fallback, field, warnings) {
52
+ if (value === undefined || value === null)
53
+ return fallback;
54
+ const n = typeof value === 'number' ? value : Number(value);
55
+ if (!Number.isFinite(n) || n <= 0 || !Number.isInteger(n)) {
56
+ warnings.push(`配置项 ${field} 不是正整数,已回落到默认值`);
57
+ return fallback;
58
+ }
59
+ return n;
60
+ }
61
+ /** 读单价配置;三个字段都必须是非负数,否则整块作废。 */
62
+ function readPricing(value, warnings) {
63
+ if (value === undefined || value === null)
64
+ return null;
65
+ if (typeof value !== 'object') {
66
+ warnings.push('配置项 pricing 不是对象,已忽略(页面只显示 token)');
67
+ return null;
68
+ }
69
+ const raw = value;
70
+ const read = (key) => {
71
+ const n = Number(raw[key]);
72
+ return Number.isFinite(n) && n >= 0 ? n : null;
73
+ };
74
+ const inputPerMTok = read('inputPerMTok');
75
+ const outputPerMTok = read('outputPerMTok');
76
+ const cacheReadPerMTok = read('cacheReadPerMTok');
77
+ if (inputPerMTok === null || outputPerMTok === null || cacheReadPerMTok === null) {
78
+ warnings.push('配置项 pricing 缺字段或不是非负数,已忽略(页面只显示 token)');
79
+ return null;
80
+ }
81
+ return { inputPerMTok, outputPerMTok, cacheReadPerMTok };
82
+ }
83
+ /** 台账文件的绝对路径。 */
84
+ export function ledgerPath(config) {
85
+ return path.join(config.dataDir, 'projects.json');
86
+ }
87
+ /** 插件工作区目录(所有运行产物都在这里)。 */
88
+ export function workspaceRoot(config) {
89
+ return path.join(config.dataDir, 'workspace');
90
+ }
91
+ /** 单个项目的工作目录。 */
92
+ export function projectDir(config, projectId) {
93
+ return path.join(workspaceRoot(config), projectId);
94
+ }
95
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/host/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 宿主 ctx 的窄化类型。
3
+ *
4
+ * cordis 的 `ctx` 在运行期是一个动态服务容器,没有可用的静态类型。与其到处 `any`,
5
+ * 不如在这里把「本插件实际用到的那几个面」逐项写出来:改宿主 API 时,
6
+ * 编译错误会集中出现在这一个文件,而不是散落几十处。
7
+ */
8
+ /** 取一个服务,取不到时返回 undefined(而不是抛)。 */
9
+ export function serviceOf(ctx, name) {
10
+ try {
11
+ const viaGet = ctx.get(name);
12
+ if (viaGet !== undefined && viaGet !== null)
13
+ return viaGet;
14
+ }
15
+ catch {
16
+ // ctx.get 在服务不存在时会抛,这里当作「没有」
17
+ }
18
+ // cordis 的 ctx 是 proxy:访问未 inject 的服务,取属性这一步本身就会抛
19
+ // 「cannot get property "x" without inject」,所以 `direct` 也必须在 try 里。
20
+ try {
21
+ const direct = ctx[name];
22
+ return direct ?? undefined;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/host/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA0MH,oCAAoC;AACpC,MAAM,UAAU,SAAS,CAAI,GAAgB,EAAE,IAAY;IACzD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,MAAW,CAAA;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;IACD,kDAAkD;IAClD,mEAAmE;IACnE,IAAI,CAAC;QACH,MAAM,MAAM,GAAI,GAA0C,CAAC,IAAI,CAAC,CAAA;QAChE,OAAQ,MAAwB,IAAI,SAAS,CAAA;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Git 探测与同步。
3
+ *
4
+ * 所有命令都走 `execFile('git', argv, {cwd})` —— 不拼 shell 字符串,分支名作为独立
5
+ * argv 传入,用户仓库里再奇怪的分支名也不会变成注入点。
6
+ *
7
+ * 只做**只读探测**与**安全同步**:
8
+ * - 探测:rev-parse / log / rev-list / status / remote
9
+ * - 同步:fetch、checkout <记录的分支>、merge --ff-only
10
+ * 永远不 reset --hard、不 clean、不 stash、不强推。工作区脏就停下来说清楚。
11
+ */
12
+ import { execFile } from 'node:child_process';
13
+ import { emptyGitSnapshot } from '../shared/types.js';
14
+ /** 单条 git 命令的超时。fetch 在慢网络上要久一点,所以给 60s。 */
15
+ const GIT_TIMEOUT_MS = 60_000;
16
+ /** 默认执行器。 */
17
+ export const gitExec = (args, cwd) => new Promise((resolve) => {
18
+ execFile('git', [...args], {
19
+ cwd,
20
+ timeout: GIT_TIMEOUT_MS,
21
+ windowsHide: true,
22
+ maxBuffer: 8 * 1024 * 1024,
23
+ // 输出按 UTF-8 解,中文提交信息才不会乱码
24
+ encoding: 'utf8',
25
+ }, (error, stdout, stderr) => {
26
+ if (error === null) {
27
+ resolve({ code: 0, stdout: String(stdout), stderr: String(stderr) });
28
+ return;
29
+ }
30
+ const code = typeof error.code === 'number' ? error.code : 1;
31
+ // 进程根本没起来(cwd 不存在、git 不在 PATH)时 stderr 是空串,
32
+ // 真正的原因只存在于 error.message 里 —— 不能因为「stderr 有值」就丢掉它
33
+ const detail = String(stderr ?? '').trim();
34
+ resolve({
35
+ code,
36
+ stdout: String(stdout ?? ''),
37
+ stderr: detail.length > 0 ? detail : error.message,
38
+ });
39
+ });
40
+ });
41
+ /**
42
+ * 探测一个目录的 Git 状态。
43
+ *
44
+ * 任何子命令失败都只让对应字段为 null,不让整个探测失败 ——
45
+ * 老 git 版本、浅克隆、没有 upstream 都是正常情况。
46
+ */
47
+ export async function probeGit(repoPath, now, exec = gitExec) {
48
+ const top = await exec(['rev-parse', '--show-toplevel'], repoPath);
49
+ if (top.code !== 0)
50
+ return emptyGitSnapshot(now);
51
+ const root = top.stdout.trim();
52
+ if (root.length === 0)
53
+ return emptyGitSnapshot(now);
54
+ const [branchRes, commitRes, logRes, upstreamRes, statusRes, remoteRes] = await Promise.all([
55
+ exec(['rev-parse', '--abbrev-ref', 'HEAD'], root),
56
+ exec(['rev-parse', 'HEAD'], root),
57
+ exec(['log', '-1', '--format=%cI%n%s'], root),
58
+ exec(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'], root),
59
+ exec(['status', '--porcelain'], root),
60
+ exec(['remote', 'get-url', 'origin'], root),
61
+ ]);
62
+ const branchRaw = branchRes.code === 0 ? branchRes.stdout.trim() : '';
63
+ const detached = branchRaw === 'HEAD' || branchRaw.length === 0;
64
+ const branch = detached ? null : branchRaw;
65
+ const commitFull = commitRes.code === 0 ? commitRes.stdout.trim() : null;
66
+ const commit = commitFull === null || commitFull.length === 0 ? null : commitFull.slice(0, 40);
67
+ const { commitTime, commitSubject } = parseLog(logRes.code === 0 ? logRes.stdout : '');
68
+ const upstream = upstreamRes.code === 0 ? emptyToNull(upstreamRes.stdout) : null;
69
+ let ahead = null;
70
+ let behind = null;
71
+ if (upstream !== null) {
72
+ const counts = await exec(['rev-list', '--left-right', '--count', 'HEAD...@{u}'], root);
73
+ if (counts.code === 0) {
74
+ const parsed = parseAheadBehind(counts.stdout);
75
+ ahead = parsed.ahead;
76
+ behind = parsed.behind;
77
+ }
78
+ }
79
+ const tree = statusRes.code === 0 ? parseTree(statusRes.stdout) : null;
80
+ const dirty = statusRes.code === 0 ? statusRes.stdout.trim().length > 0 : null;
81
+ return {
82
+ isRepo: true,
83
+ root,
84
+ branch,
85
+ detached,
86
+ commit,
87
+ commitFull: commitFull === null || commitFull.length === 0 ? null : commitFull,
88
+ commitTime,
89
+ commitSubject,
90
+ upstream,
91
+ ahead,
92
+ behind,
93
+ dirty,
94
+ tree,
95
+ remoteUrl: remoteRes.code === 0 ? emptyToNull(remoteRes.stdout) : null,
96
+ checkedAt: now,
97
+ };
98
+ }
99
+ /**
100
+ * 解析 `git status --porcelain` 的 XY 两列:
101
+ * - X = 暂存区状态,Y = 工作区状态;`??` 是未跟踪;
102
+ * - 只看计数,不看文件名(页面只需要「有改动 / 几处」)。
103
+ */
104
+ export function parseTree(stdout) {
105
+ let staged = 0;
106
+ let unstaged = 0;
107
+ let untracked = 0;
108
+ for (const line of stdout.split(/\r?\n/)) {
109
+ if (line.trim().length === 0)
110
+ continue;
111
+ const x = line[0] ?? ' ';
112
+ const y = line[1] ?? ' ';
113
+ if (x === '?' && y === '?') {
114
+ untracked += 1;
115
+ continue;
116
+ }
117
+ if (x !== ' ' && x !== '?')
118
+ staged += 1;
119
+ if (y !== ' ' && y !== '?')
120
+ unstaged += 1;
121
+ }
122
+ return { staged, unstaged, untracked };
123
+ }
124
+ /** `log -1 --format=%cI%n%s` 的两行结果。 */
125
+ export function parseLog(stdout) {
126
+ const lines = stdout.split(/\r?\n/);
127
+ const time = lines[0]?.trim() ?? '';
128
+ const subject = lines.slice(1).join(' ').trim();
129
+ return {
130
+ commitTime: time.length > 0 ? time : null,
131
+ commitSubject: subject.length > 0 ? subject : null,
132
+ };
133
+ }
134
+ /**
135
+ * `rev-list --left-right --count HEAD...@{u}` 的输出是 `<HEAD 独有>\t<上游独有>`,
136
+ * 也就是 `<ahead>\t<behind>`。方向搞反会让页面把「落后 3 个提交」显示成「领先 3 个」,
137
+ * 所以这里单独抽出来测。
138
+ */
139
+ export function parseAheadBehind(stdout) {
140
+ const m = /^\s*(\d+)\s+(\d+)\s*$/.exec(stdout);
141
+ if (m === null)
142
+ return { ahead: null, behind: null };
143
+ return { ahead: Number(m[1]), behind: Number(m[2]) };
144
+ }
145
+ /**
146
+ * 把仓库切回记录的分支并快进到最新提交。
147
+ *
148
+ * 顺序:脏工作区先停 → fetch(有 origin 才做,失败只记录)→ checkout → merge --ff-only → 重新探测。
149
+ */
150
+ export async function syncToLatest(repoPath, recordedBranch, exec = gitExec) {
151
+ const before = await probeGit(repoPath, new Date().toISOString(), exec);
152
+ if (!before.isRepo || before.root === null) {
153
+ return { skipped: true, reason: 'no-git', from: null, to: null };
154
+ }
155
+ const root = before.root;
156
+ const from = before.commit;
157
+ if (before.dirty === true) {
158
+ return { skipped: true, reason: 'dirty', from, to: from };
159
+ }
160
+ if (before.remoteUrl !== null) {
161
+ // fetch 失败(离线、无权限)不该阻断后续 checkout/ff:本地可能已经是最新的
162
+ await exec(['fetch', '--all', '--prune'], root);
163
+ }
164
+ const target = recordedBranch ?? before.branch;
165
+ if (target !== null && target !== before.branch) {
166
+ const checkout = await exec(['checkout', target], root);
167
+ if (checkout.code !== 0) {
168
+ return { skipped: true, reason: 'checkout-failed', from, to: from };
169
+ }
170
+ }
171
+ const afterCheckout = await probeGit(repoPath, new Date().toISOString(), exec);
172
+ if (afterCheckout.upstream !== null) {
173
+ const merge = await exec(['merge', '--ff-only', '@{u}'], root);
174
+ if (merge.code !== 0) {
175
+ return { skipped: true, reason: 'ff-failed', from, to: afterCheckout.commit };
176
+ }
177
+ }
178
+ const after = await probeGit(repoPath, new Date().toISOString(), exec);
179
+ return { skipped: false, reason: null, from, to: after.commit };
180
+ }
181
+ function emptyToNull(value) {
182
+ const trimmed = value.trim();
183
+ return trimmed.length === 0 ? null : trimmed;
184
+ }
185
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/host/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAoC,MAAM,oBAAoB,CAAA;AAYvF,4CAA4C;AAC5C,MAAM,cAAc,GAAG,MAAM,CAAA;AAE7B,aAAa;AACb,MAAM,CAAC,MAAM,OAAO,GAAY,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAC5C,IAAI,OAAO,CAAY,CAAC,OAAO,EAAE,EAAE;IACjC,QAAQ,CACN,KAAK,EACL,CAAC,GAAG,IAAI,CAAC,EACT;QACE,GAAG;QACH,OAAO,EAAE,cAAc;QACvB,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;QAC1B,0BAA0B;QAC1B,QAAQ,EAAE,MAAM;KACjB,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;QACxB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YACpE,OAAM;QACR,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAC5D,4CAA4C;QAC5C,mDAAmD;QACnD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QAC1C,OAAO,CAAC;YACN,IAAI;YACJ,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;YAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO;SACnD,CAAC,CAAA;IACJ,CAAC,CACF,CAAA;AACH,CAAC,CAAC,CAAA;AAEJ;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,QAAgB,EAChB,GAAW,EACX,OAAgB,OAAO;IAEvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,QAAQ,CAAC,CAAA;IAClE,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IAC9B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAEnD,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC1F,IAAI,CAAC,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC;QACjD,IAAI,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC;QACjC,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,kBAAkB,CAAC,EAAE,IAAI,CAAC;QAC7C,IAAI,CAAC,CAAC,WAAW,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC;QACzE,IAAI,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC;QACrC,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC;KAC5C,CAAC,CAAA;IAEF,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACrE,MAAM,QAAQ,GAAG,SAAS,KAAK,MAAM,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,CAAA;IAC/D,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;IAE1C,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IACxE,MAAM,MAAM,GAAG,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAE9F,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAEtF,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAEhF,IAAI,KAAK,GAAkB,IAAI,CAAA;IAC/B,IAAI,MAAM,GAAkB,IAAI,CAAA;IAChC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,CAAA;QACvF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC9C,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;YACpB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;QACxB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACtE,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAE9E,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,IAAI;QACJ,MAAM;QACN,QAAQ;QACR,MAAM;QACN,UAAU,EAAE,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU;QAC9E,UAAU;QACV,aAAa;QACb,QAAQ;QACR,KAAK;QACL,MAAM;QACN,KAAK;QACL,IAAI;QACJ,SAAS,EAAE,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI;QACtE,SAAS,EAAE,GAAG;KACf,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACzC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;YAAE,SAAQ;QACtC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAA;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAA;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YAC3B,SAAS,IAAI,CAAC,CAAA;YACd,SAAQ;QACV,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,MAAM,IAAI,CAAC,CAAA;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,QAAQ,IAAI,CAAC,CAAA;IAC3C,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAA;AACxC,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,QAAQ,CAAC,MAAc;IAIrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACnC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IACnC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;IAC/C,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QACzC,aAAa,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;KACnD,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,CAAC,GAAG,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC9C,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IACpD,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACtD,CAAC;AAUD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAgB,EAChB,cAA6B,EAC7B,OAAgB,OAAO;IAEvB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAA;IACvE,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;IAClE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;IACxB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAA;IAE1B,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;IAC3D,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QAC9B,gDAAgD;QAChD,MAAM,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAA;IACjD,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,IAAI,MAAM,CAAC,MAAM,CAAA;IAC9C,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QAChD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAA;QACvD,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;QACrE,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAA;IAC9E,IAAI,aAAa,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAA;QAC9D,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,aAAa,CAAC,MAAM,EAAE,CAAA;QAC/E,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAA;IACtE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,CAAA;AACjE,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAA;AAC9C,CAAC"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * 路由层公共设施:请求体解析、统一响应、信任栅栏。
3
+ *
4
+ * 三条硬约束(都是这一层唯一的正确写法):
5
+ * 1. 响应只从 `sendJson` / `sendError` 出,且不重复写头;
6
+ * 2. 对外错误只回稳定 code + 一句人话,原始 message(常含绝对路径)只进日志;
7
+ * 3. 请求体必须是 JSON 对象;空 body / 数组 / 标量在 `readJsonObject` 就抛。
8
+ */
9
+ import { serviceOf } from './context.js';
10
+ /** 请求体上限:本插件只收短 JSON。 */
11
+ const MAX_BODY_BYTES = 256 * 1024;
12
+ /** 解析失败的类型,路由层据此回 400。 */
13
+ export class BadRequest extends Error {
14
+ code;
15
+ constructor(message, code = 'bad-request') {
16
+ super(message);
17
+ this.name = 'BadRequest';
18
+ this.code = code;
19
+ }
20
+ }
21
+ /** 读完整请求体并解析成 JSON 对象。 */
22
+ export async function readJsonObject(req) {
23
+ const raw = await readBody(req);
24
+ if (raw.trim().length === 0)
25
+ return {};
26
+ let parsed;
27
+ try {
28
+ parsed = JSON.parse(raw);
29
+ }
30
+ catch {
31
+ throw new BadRequest('请求体不是合法 JSON');
32
+ }
33
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
34
+ throw new BadRequest('请求体必须是 JSON 对象');
35
+ }
36
+ return parsed;
37
+ }
38
+ async function readBody(req) {
39
+ const chunks = [];
40
+ let total = 0;
41
+ for await (const chunk of req) {
42
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
43
+ total += buf.length;
44
+ if (total > MAX_BODY_BYTES)
45
+ throw new BadRequest('请求体过大');
46
+ chunks.push(buf);
47
+ }
48
+ return Buffer.concat(chunks).toString('utf8');
49
+ }
50
+ /** 唯一响应出口:已发过就什么都不做。 */
51
+ export function sendJson(res, status, payload) {
52
+ if (res.headersSent || res.writableEnded)
53
+ return false;
54
+ try {
55
+ res.writeHead(status, {
56
+ 'Content-Type': 'application/json; charset=utf-8',
57
+ 'Cache-Control': 'no-store',
58
+ });
59
+ res.end(JSON.stringify(payload));
60
+ return true;
61
+ }
62
+ catch {
63
+ try {
64
+ res.destroy();
65
+ }
66
+ catch {
67
+ // 连接已经没了,无事可做
68
+ }
69
+ return false;
70
+ }
71
+ }
72
+ /** 成功响应。 */
73
+ export function sendOk(res, value) {
74
+ return sendJson(res, 200, { ok: true, value });
75
+ }
76
+ /** 错误响应。`err` 只进日志,不回给浏览器。 */
77
+ export function sendError(res, status, code, message, err) {
78
+ const detail = err === undefined ? '' : ` | ${describe(err)}`;
79
+ console.warn(`[dsh-archify-projects] ${code}: ${message}${detail}`);
80
+ return sendJson(res, status, { ok: false, error: { code, message } });
81
+ }
82
+ function describe(err) {
83
+ if (err instanceof Error)
84
+ return err.message;
85
+ try {
86
+ return typeof err === 'string' ? err : JSON.stringify(err);
87
+ }
88
+ catch {
89
+ return String(err);
90
+ }
91
+ }
92
+ /** 请求方法(大写)。 */
93
+ export function methodOf(req) {
94
+ return (req.method ?? 'GET').toUpperCase();
95
+ }
96
+ /** 请求路径(去掉 query)。 */
97
+ export function pathOf(req) {
98
+ const url = req.url ?? '/';
99
+ const index = url.indexOf('?');
100
+ return index === -1 ? url : url.slice(0, index);
101
+ }
102
+ /**
103
+ * 信任栅栏。
104
+ *
105
+ * 三道,任意一道不过即 403:
106
+ * 1. `sec-fetch-site` 存在且不是同源/同站/直接输入;
107
+ * 2. `Origin` 存在且 host 与请求 host 不一致;
108
+ * 3. 宿主自己的 `connection.requestRejection`(若该服务存在)。
109
+ *
110
+ * 浏览器直连(无 Origin / 无 sec-fetch-site,如 curl)交给第 3 道与路由自身鉴权判断,
111
+ * 本插件没有敏感凭据可泄露,默认放行。
112
+ */
113
+ export function makeFence(ctx) {
114
+ return (req) => {
115
+ const site = firstHeader(req.headers['sec-fetch-site']);
116
+ if (site !== null && !['same-origin', 'same-site', 'none'].includes(site)) {
117
+ return '跨站请求被拒绝';
118
+ }
119
+ const hostHeader = firstHeader(req.headers.host);
120
+ if (hostHeader === null)
121
+ return '请求缺少 Host 头';
122
+ const origin = firstHeader(req.headers.origin);
123
+ if (origin !== null) {
124
+ let originHost = null;
125
+ try {
126
+ originHost = new URL(origin).host.toLowerCase();
127
+ }
128
+ catch {
129
+ return 'Origin 无法解析';
130
+ }
131
+ if (originHost !== hostHeader.toLowerCase())
132
+ return 'Origin 与请求 host 不一致';
133
+ }
134
+ const connection = serviceOf(ctx, 'connection');
135
+ if (connection !== undefined && typeof connection.requestRejection === 'function') {
136
+ const rejected = connection.requestRejection(req);
137
+ if (rejected !== undefined && rejected !== null && rejected !== false) {
138
+ // 诊断信息:宿主连接的准入会同时做「可信 authority」和「浏览器会话认证」两件事,
139
+ // 401 与 403 的处理方式完全不同,光看一条文案分不出来。
140
+ try {
141
+ ctx.logger.warn(`[dsh-archify-projects] fence 拒绝 code=${String(rejected)} host=${hostHeader} origin=${origin ?? '-'} site=${site ?? '-'} method=${req.method ?? '-'} url=${req.url ?? '-'}`);
142
+ }
143
+ catch {
144
+ // 忽略日志失败
145
+ }
146
+ return '宿主拒绝该请求';
147
+ }
148
+ }
149
+ return null;
150
+ };
151
+ }
152
+ function firstHeader(value) {
153
+ if (value === undefined)
154
+ return null;
155
+ const raw = Array.isArray(value) ? value[0] : value;
156
+ if (raw === undefined)
157
+ return null;
158
+ const trimmed = raw.trim();
159
+ return trimmed.length === 0 ? null : trimmed;
160
+ }
161
+ /**
162
+ * 匹配 `/projects/:id/check` 这类路径。
163
+ *
164
+ * 只支持 `:name` 段——本插件的路由表就这么浅,上正则引擎是过度设计。
165
+ */
166
+ export function matchPath(pattern, pathname) {
167
+ const patternParts = pattern.split('/').filter((p) => p.length > 0);
168
+ const pathParts = pathname.split('/').filter((p) => p.length > 0);
169
+ if (patternParts.length !== pathParts.length)
170
+ return null;
171
+ const params = {};
172
+ for (let i = 0; i < patternParts.length; i += 1) {
173
+ const patternPart = patternParts[i];
174
+ const pathPart = pathParts[i];
175
+ if (patternPart.startsWith(':')) {
176
+ params[patternPart.slice(1)] = decodeURIComponent(pathPart);
177
+ continue;
178
+ }
179
+ if (patternPart !== pathPart)
180
+ return null;
181
+ }
182
+ return params;
183
+ }
184
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/host/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * 宿主插件入口:`dsh-archify-projects`。
3
+ *
4
+ * 这里只做装配,不含业务逻辑:
5
+ * ① 解析配置、准备台账与工作区;
6
+ * ② 注册一条 prefix HTTP 路由,把所有请求交给 routes.ts 的分发器;
7
+ * ③ 把 archify skill 目录做一次懒解析并缓存(preset 侧与提示词侧共用同一份答案)。
8
+ *
9
+ * 页面由 `dist/client.js` 提供(package.json 的 `exports["./client"]`),
10
+ * 受限于 preset 的那个受限 agent 组合由 `lib/host/preset.js` 提供。
11
+ */
12
+ import path from 'node:path';
13
+ import { ROUTE_PREFIX } from '../shared/protocol.js';
14
+ import { resolveArchifySkillDir } from './archify-skill.js';
15
+ import { ledgerPath, resolveConfig, workspaceRoot } from './config.js';
16
+ import { serviceOf } from './context.js';
17
+ import { makeFence } from './http.js';
18
+ import { createDispatcher } from './routes.js';
19
+ import { reconcileArtifacts, Runner } from './runner.js';
20
+ import { ProjectStore } from './store.js';
21
+ import { ensureWorkspace } from './workspace.js';
22
+ /** 与 package.json 的 version 保持一致(有单测守着)。 */
23
+ export const VERSION = '0.1.0';
24
+ /** 插件入口。 */
25
+ export function apply(ctx, rawConfig) {
26
+ const warnings = [];
27
+ const config = resolveConfig(rawConfig, warnings);
28
+ for (const warning of warnings)
29
+ log(ctx, 'warn', warning);
30
+ const store = new ProjectStore(ledgerPath(config));
31
+ let cachedSkill;
32
+ const skillDir = () => {
33
+ cachedSkill ??= resolveArchifySkillDir(config.archifySkillDir);
34
+ return cachedSkill;
35
+ };
36
+ const runner = new Runner({ ctx, store, config, skillDir });
37
+ const hasSessionService = serviceOf(ctx, 'sessionController') !== undefined || serviceOf(ctx, 'agents') !== undefined;
38
+ if (!hasSessionService) {
39
+ log(ctx, 'warn', '宿主没有 sessionController / agents 服务:更新会失败,只能浏览台账');
40
+ }
41
+ const dispatcher = createDispatcher({
42
+ ctx,
43
+ store,
44
+ config,
45
+ runner,
46
+ fence: makeFence(ctx),
47
+ skillDir,
48
+ version: VERSION,
49
+ hasSessionService,
50
+ });
51
+ // 路由必须挂在 inject 出来的子 ctx 上:cordis 的 proxy 在没 inject 时读
52
+ // `ctx.webServer` 会直接抛,注册就会被静默跳过(页面表现为「HTTP 404」)。
53
+ ctx.inject(['webServer'], (scoped) => {
54
+ scoped.effect(() => scoped.webServer.register({
55
+ kind: 'prefix',
56
+ path: ROUTE_PREFIX,
57
+ handler: (req, res) => {
58
+ void dispatcher(req, res);
59
+ },
60
+ }), 'dsh-archify-projects: http routes');
61
+ log(ctx, 'info', `HTTP 路由已注册:${ROUTE_PREFIX}`);
62
+ });
63
+ // 开库对账:把上次没记完的产物认回来(宿主重启/断连后最常见的「明明有图却显示失败」)
64
+ void reconcileArtifacts(store, config).then((adopted) => {
65
+ for (const line of adopted)
66
+ log(ctx, 'info', `已认领磁盘上的产物:${line}`);
67
+ }, (err) => log(ctx, 'warn', `产物对账失败:${describe(err)}`));
68
+ // 工作区准备是异步的,不阻塞宿主启动;失败也不致命
69
+ void ensureWorkspace(ctx, config, {
70
+ warn: (message) => log(ctx, 'warn', message),
71
+ }).then((info) => {
72
+ log(ctx, 'info', `工作区就绪:${path.basename(info.root)}(${ROUTE_PREFIX})`);
73
+ const drained = store.drainWarnings();
74
+ for (const warning of drained)
75
+ log(ctx, 'warn', warning);
76
+ }, (err) => log(ctx, 'warn', `准备插件工作区失败:${describe(err)}`));
77
+ log(ctx, 'info', `已挂载 v${VERSION},台账 ${ledgerPath(config)},工作区 ${workspaceRoot(config)}`);
78
+ }
79
+ function log(ctx, level, message) {
80
+ try {
81
+ ctx.logger[level](`[dsh-archify-projects] ${message}`);
82
+ }
83
+ catch {
84
+ // 日志器不可用不影响功能
85
+ }
86
+ }
87
+ function describe(err) {
88
+ return err instanceof Error ? err.message : String(err);
89
+ }
90
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"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"}