@co0ontty/wand 4.57.0 → 4.58.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.
- package/dist/build-info.json +3 -3
- package/dist/ensure-node-pty-helper.d.ts +0 -1
- package/dist/ensure-node-pty-helper.js +0 -5
- package/dist/git-quick-commit.js +18 -19
- package/dist/github-connector.d.ts +11 -5
- package/dist/github-connector.js +105 -33
- package/dist/missions.d.ts +1 -1
- package/dist/missions.js +17 -4
- package/dist/server-file-routes.js +43 -2
- package/dist/server-github-routes.js +50 -53
- package/dist/server-mission-routes.js +11 -10
- package/dist/server-task-routes.d.ts +14 -2
- package/dist/server-task-routes.js +248 -84
- package/dist/server-workspace-routes.js +90 -16
- package/dist/server.js +2 -20
- package/dist/storage.d.ts +23 -6
- package/dist/storage.js +130 -53
- package/dist/structured-exec-host.d.ts +13 -2
- package/dist/structured-exec-host.js +71 -36
- package/dist/structured-exec-pump.d.ts +1 -1
- package/dist/structured-exec-pump.js +14 -8
- package/dist/task-types.d.ts +15 -0
- package/dist/terminal-daemon-client.d.ts +2 -0
- package/dist/terminal-daemon-client.js +7 -1
- package/dist/terminal-daemon-server.js +17 -18
- package/dist/tui/ipc-server.js +3 -1
- package/dist/types.d.ts +1 -0
- package/dist/wand-task-sync.d.ts +9 -0
- package/dist/wand-task-sync.js +50 -0
- package/dist/web-ui/content/scripts.js +85 -85
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/dist/ws-broadcast.js +5 -1
- package/package.json +2 -3
- package/dist/taskboard-bridge.d.ts +0 -11
- package/dist/taskboard-bridge.js +0 -371
- package/vendor/codex-taskboard/LICENSE +0 -201
- package/vendor/codex-taskboard/dist/web/assets/AiChat-BNn4VJ5a.js +0 -12
- package/vendor/codex-taskboard/dist/web/assets/GanttView-D8wnfGrG.css +0 -1
- package/vendor/codex-taskboard/dist/web/assets/GanttView-rGWhuEX9.js +0 -39
- package/vendor/codex-taskboard/dist/web/assets/abnfDiagram-N423BO3Z-DbFG6lbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/arc-BjSQqbzd.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/architecture-TIHT7OUA-C9-t8p4o.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/architectureDiagram-T3A2C74G-Db4kJZwO.js +0 -36
- package/vendor/codex-taskboard/dist/web/assets/array-BifhSqXX.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/blockDiagram-VBNYF7ZC-C-BywuWV.js +0 -132
- package/vendor/codex-taskboard/dist/web/assets/c4Diagram-5PPSVZJV-Dq-80rjP.js +0 -10
- package/vendor/codex-taskboard/dist/web/assets/channel-C7mnz8Xc.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-2GRJ4B5K-B2G57us9.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-3NCLNEKW-DVGIRib_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-4I5QYGJK-kNoGFqr0.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-5RXB4S5H-4iv6-xM6.js +0 -231
- package/vendor/codex-taskboard/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +0 -15
- package/vendor/codex-taskboard/dist/web/assets/chunk-6Q2QTUOP-1J34vxHr.js +0 -88
- package/vendor/codex-taskboard/dist/web/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-7Z6QIM7H-CDQpQlxX.js +0 -10
- package/vendor/codex-taskboard/dist/web/assets/chunk-GF5L2VYU-Dg92oHHl.js +0 -206
- package/vendor/codex-taskboard/dist/web/assets/chunk-I66GZJ75-Bk8_gFs2.js +0 -125
- package/vendor/codex-taskboard/dist/web/assets/chunk-J7OUQ5F2-CJRcenlr.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/chunk-JQJVKLGR-BLBV_Sus.js +0 -156
- package/vendor/codex-taskboard/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-KBJHAD2P-Da9Yjz0p.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-KEIR6QF5-Dj-OpFgW.js +0 -161
- package/vendor/codex-taskboard/dist/web/assets/chunk-NSK5VX7P-Cb56tnGK.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/chunk-QR6OTTB3-BwoszTxn.js +0 -62
- package/vendor/codex-taskboard/dist/web/assets/chunk-RYQCIY6F-D_L2RdcQ.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-UBXNYLIW-YX5mN8cl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-W5SLKNZC-DlOLhhDO.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-WRU74C26-B7SBmP0K.js +0 -70
- package/vendor/codex-taskboard/dist/web/assets/chunk-XXDRQBXY-BuE3VzE_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-ZIRB5QZD-C6fEPe3t.js +0 -32
- package/vendor/codex-taskboard/dist/web/assets/classDiagram-JCYQIIEL-KIdCVvbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/classDiagram-v2-OCEON4UE-KIdCVvbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cose-bilkent-JH36ORCC-BzQvy5TC.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cynefin-VYW2F7L2-D1_5O1XR.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cynefinDiagram-MW4NZA55-DOW_U5QN.js +0 -62
- package/vendor/codex-taskboard/dist/web/assets/cytoscape.esm-DnGd5Fuo.js +0 -321
- package/vendor/codex-taskboard/dist/web/assets/dagre-VZM6K2ZE-0Dntc3NT.js +0 -4
- package/vendor/codex-taskboard/dist/web/assets/dagre-dpRSp0QF.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/defaultLocale-C8Fc0cco.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/diagram-7IWD3JNH-D7GleYAM.js +0 -30
- package/vendor/codex-taskboard/dist/web/assets/diagram-B4RE2ZJO-DCXTe6q5.js +0 -3
- package/vendor/codex-taskboard/dist/web/assets/diagram-LBJQPF4R-ZFJyXEXl.js +0 -24
- package/vendor/codex-taskboard/dist/web/assets/diagram-Q27KOJAE-DUGCuGd9.js +0 -24
- package/vendor/codex-taskboard/dist/web/assets/diagram-UB23O5K3-DRmp4Nng.js +0 -41
- package/vendor/codex-taskboard/dist/web/assets/dist-D9sYb5Oa.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ebnfDiagram-BXEA7PRR-CkKoaf7T.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/erDiagram-JOGREHBK-nBX5z4R4.js +0 -85
- package/vendor/codex-taskboard/dist/web/assets/eventmodeling-45OFAUF4-pexmK65N.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/flowDiagram-UKHOOZJN-aroohRbL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ganttDiagram-PKOTCBZU-BOJWUTXa.js +0 -292
- package/vendor/codex-taskboard/dist/web/assets/gitGraph-TEB2WS4Q-DxnuhSZU.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/gitGraphDiagram-DS77QQ5N-Cte975TC.js +0 -106
- package/vendor/codex-taskboard/dist/web/assets/graphlib-DS17s2tU.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/index-Docmx4uQ.css +0 -1
- package/vendor/codex-taskboard/dist/web/assets/index-Du07lEGz.js +0 -121
- package/vendor/codex-taskboard/dist/web/assets/info-DKCQHKI2-DOTjgYxk.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/infoDiagram-6WML65LV-SpyV9UJd.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/init-D6jRqBbL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ishikawaDiagram-WSZJBQD7-T0PZwoUZ.js +0 -70
- package/vendor/codex-taskboard/dist/web/assets/journeyDiagram-NVQOT4AX-B88b8UgX.js +0 -139
- package/vendor/codex-taskboard/dist/web/assets/kanban-definition-27J2QSJJ-CIlfNnl_.js +0 -89
- package/vendor/codex-taskboard/dist/web/assets/katex-B7rAX3Vi.js +0 -257
- package/vendor/codex-taskboard/dist/web/assets/line-fWrwPZhB.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/linear-B7l8qgEw.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/map-BaFkSB1l.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/mermaid-parser.core-C5gls2nC.js +0 -7
- package/vendor/codex-taskboard/dist/web/assets/mermaid.core-POz2Xcvr.js +0 -11
- package/vendor/codex-taskboard/dist/web/assets/mindmap-definition-FAOFIHXS-BM0-egZ5.js +0 -96
- package/vendor/codex-taskboard/dist/web/assets/ordinal-hYBb2elL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/packet-7NZHBO7P-DQZzyVos.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/path-BWPyau1x.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pegDiagram-VL7TDLO6-B8aEwl-7.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pie-RZYD4A2V-DRUflilR.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pieDiagram-7S7Q4E2Y-0cDDxlua.js +0 -39
- package/vendor/codex-taskboard/dist/web/assets/purify.es-Bwt4ulIK.js +0 -3
- package/vendor/codex-taskboard/dist/web/assets/quadrantDiagram-CIZ2JOQS-CBgb0YZ0.js +0 -7
- package/vendor/codex-taskboard/dist/web/assets/radar-I7S5WNFK-tRocoKxq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-3IZDKUUU-DaS4ZlX_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-abnf-AHOZXSZD-BN1T5dBT.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-ebnf-EBAXGLYW-Cuf8qyKs.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-peg-LSFZ7HO6-B511_EYq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroadDiagram-AXF67PYL-QtPo-JZb.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/requirementDiagram-LRYGKXZP-CuBIF85r.js +0 -84
- package/vendor/codex-taskboard/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/rough.esm-CSKSodPl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/sankeyDiagram-W5VNT64P-CRA-b_za.js +0 -40
- package/vendor/codex-taskboard/dist/web/assets/sequenceDiagram-SI44F4Z6-CoddVWwY.js +0 -162
- package/vendor/codex-taskboard/dist/web/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/src-_wZywoZs.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/stateDiagram-OKZ733FA-D8DMM2ln.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/stateDiagram-v2-UEYNNEHI-CVp0eYAS.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/swimlanes-SLNWSIFB-C1fC7EFu.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CPd58Xso.js +0 -8
- package/vendor/codex-taskboard/dist/web/assets/timeline-definition-Z64GVDOM-BUBv8aKP.js +0 -120
- package/vendor/codex-taskboard/dist/web/assets/treeView-QDETBFTQ-C-Bve2kY.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/treemap-6X3UGDF4-BcphHDdV.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/vennDiagram-T6HMQDX7-0wQnwAyA.js +0 -34
- package/vendor/codex-taskboard/dist/web/assets/wardley-OPB4EBWU-CJZ4BevB.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/wardleyDiagram-T6FBY63Y-U9nwPrkN.js +0 -78
- package/vendor/codex-taskboard/dist/web/assets/xychartDiagram-ELKLHX3M-CyCqnzgE.js +0 -7
- package/vendor/codex-taskboard/dist/web/codex-agent-logo.png +0 -0
- package/vendor/codex-taskboard/dist/web/codex-app-icon.png +0 -0
- package/vendor/codex-taskboard/dist/web/favicon.svg +0 -7
- package/vendor/codex-taskboard/dist/web/index.html +0 -20
- package/vendor/codex-taskboard/server/ai-chat-catalog.mjs +0 -1031
- package/vendor/codex-taskboard/server/ai-chat-process.mjs +0 -462
- package/vendor/codex-taskboard/server/ai-chat.mjs +0 -1237
- package/vendor/codex-taskboard/server/ai-turn-owner.mjs +0 -39
- package/vendor/codex-taskboard/server/app.mjs +0 -3211
- package/vendor/codex-taskboard/server/cloud-config.mjs +0 -188
- package/vendor/codex-taskboard/server/cloud-proxy.mjs +0 -310
- package/vendor/codex-taskboard/server/codex-app-server.mjs +0 -376
- package/vendor/codex-taskboard/server/codex-slash-commands-0.139.0.json +0 -62
- package/vendor/codex-taskboard/server/database.mjs +0 -2845
- package/vendor/codex-taskboard/server/index.mjs +0 -41
- package/vendor/codex-taskboard/server/jira-config.mjs +0 -181
- package/vendor/codex-taskboard/server/jira-integration.mjs +0 -487
- package/vendor/codex-taskboard/server/project-summary.mjs +0 -159
- package/vendor/codex-taskboard/shared/api-fields.mjs +0 -163
- package/vendor/codex-taskboard/shared/codex-environment.mjs +0 -5
- package/vendor/codex-taskboard/shared/codex-executable.mjs +0 -77
- package/vendor/codex-taskboard/shared/codex-permissions.mjs +0 -9
- package/vendor/codex-taskboard/shared/composer-reference.d.mts +0 -19
- package/vendor/codex-taskboard/shared/composer-reference.mjs +0 -90
- package/vendor/codex-taskboard/shared/domain.mjs +0 -34
- package/vendor/codex-taskboard/shared/executable-command.mjs +0 -13
- package/vendor/codex-taskboard/shared/process-tree.mjs +0 -27
- package/vendor/codex-taskboard/shared/task-input.mjs +0 -308
- package/vendor/codex-taskboard/shared/task-records.mjs +0 -199
- package/vendor/codex-taskboard/shared/task-relations.mjs +0 -55
- package/vendor/codex-taskboard/shared/taskboard-automation.mjs +0 -277
- package/vendor/codex-taskboard/skills/manage-taskboard/SKILL.md +0 -45
- package/vendor/codex-taskboard/skills/manage-taskboard/agents/openai.yaml +0 -4
- package/vendor/codex-taskboard/skills/manage-taskboard/references/cli.md +0 -211
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
3
|
-
"builtAt": "2026-09-
|
|
4
|
-
"version": "4.
|
|
2
|
+
"commit": "3ab96a356a555f419adb4ec4c5cba63dfd170a5c",
|
|
3
|
+
"builtAt": "2026-09-11T13:51:01.381Z",
|
|
4
|
+
"version": "4.58.0",
|
|
5
5
|
"channel": "stable"
|
|
6
6
|
}
|
|
@@ -6,5 +6,4 @@ export interface SpawnHelperRepair {
|
|
|
6
6
|
/** Repair one spawn-helper file. Exported for tests. */
|
|
7
7
|
export declare function repairSpawnHelperFile(vendorPath: string): SpawnHelperRepair;
|
|
8
8
|
export declare function ensureNodePtyHelperExecutable(): void;
|
|
9
|
-
export declare function resetNodePtyHelperStateForTests(): void;
|
|
10
9
|
export declare function describePtySpawnFailure(error: unknown): string;
|
|
@@ -141,11 +141,6 @@ export function ensureNodePtyHelperExecutable() {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
|
-
export function resetNodePtyHelperStateForTests() {
|
|
145
|
-
cachedVendorHelperPath = null;
|
|
146
|
-
patchedVendorHelperPath = null;
|
|
147
|
-
patchedExecutableHelperPath = null;
|
|
148
|
-
}
|
|
149
144
|
export function describePtySpawnFailure(error) {
|
|
150
145
|
const message = getErrorMessage(error, "无法启动终端。");
|
|
151
146
|
if (/posix_spawnp failed/i.test(message)) {
|
package/dist/git-quick-commit.js
CHANGED
|
@@ -618,29 +618,33 @@ export async function callConfiguredAiText(prompt, cwd, language, opts) {
|
|
|
618
618
|
}
|
|
619
619
|
return callCliAiText(prompt, cwd, language, opts);
|
|
620
620
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
621
|
+
/** Read the unstaged + staged tree without touching the index. */
|
|
622
|
+
async function collectWorkingTreeDiff(cwd) {
|
|
623
|
+
const candidates = [
|
|
624
|
+
[["diff", "HEAD", "--submodule=log"], 5000],
|
|
625
|
+
[["diff", "--cached", "--submodule=log"], 5000],
|
|
626
|
+
[["diff", "HEAD", "--name-only"], 3000],
|
|
627
|
+
];
|
|
628
|
+
let diff = "";
|
|
629
|
+
for (const [args, timeout] of candidates) {
|
|
630
630
|
try {
|
|
631
|
-
diff = await runGitAsync(
|
|
631
|
+
diff = await runGitAsync(args, cwd, timeout);
|
|
632
632
|
}
|
|
633
633
|
catch {
|
|
634
|
-
diff = "
|
|
634
|
+
diff = "";
|
|
635
635
|
}
|
|
636
|
+
if (diff)
|
|
637
|
+
break;
|
|
636
638
|
}
|
|
639
|
+
if (!diff)
|
|
640
|
+
diff = "(no diff available)";
|
|
637
641
|
if (diff.length > MAX_DIFF_FOR_AI) {
|
|
638
642
|
diff = diff.slice(0, MAX_DIFF_FOR_AI) + "\n\n... (diff truncated) ...";
|
|
639
643
|
}
|
|
640
644
|
return diff;
|
|
641
645
|
}
|
|
642
646
|
async function generateCommitMessage(cwd, language, ai = {}) {
|
|
643
|
-
const diff = await
|
|
647
|
+
const diff = await collectWorkingTreeDiff(cwd);
|
|
644
648
|
const lang = language.trim() || "中文";
|
|
645
649
|
const prompt = `阅读以下 git diff,用${lang}写一条简洁的 commit message。要求:祈使句,不超过 50 字,描述「做了什么」。只输出 message 本身,不要引号、不要 Markdown 格式、不要任何额外说明。\n\n${diff}`;
|
|
646
650
|
const raw = await callConfiguredAiText(prompt, cwd, language, ai);
|
|
@@ -678,7 +682,7 @@ function sanitizeSuggestedTag(value) {
|
|
|
678
682
|
return cleaned;
|
|
679
683
|
}
|
|
680
684
|
async function generateCommitMessageWithTag(cwd, language, ai = {}) {
|
|
681
|
-
const diff = await
|
|
685
|
+
const diff = await collectWorkingTreeDiff(cwd);
|
|
682
686
|
let latestTag;
|
|
683
687
|
try {
|
|
684
688
|
latestTag = await runGitAsync(["describe", "--tags", "--abbrev=0"], cwd) || undefined;
|
|
@@ -721,12 +725,7 @@ export async function generateCommitMessageOnly(cwd, language, ai = {}) {
|
|
|
721
725
|
if (!cwd || !existsSync(cwd)) {
|
|
722
726
|
throw new QuickCommitError("工作目录不存在。", "CWD_MISSING");
|
|
723
727
|
}
|
|
724
|
-
|
|
725
|
-
await runGitAsync(["add", "-A"], cwd, 5000);
|
|
726
|
-
}
|
|
727
|
-
catch {
|
|
728
|
-
// best-effort staging so the diff is complete
|
|
729
|
-
}
|
|
728
|
+
// Read the working tree as-is. Generating a message must not stage files.
|
|
730
729
|
return generateCommitMessageWithTag(cwd, language, ai);
|
|
731
730
|
}
|
|
732
731
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { WandStorage } from "./storage.js";
|
|
2
2
|
export declare const GITHUB_PROVIDER = "github";
|
|
3
3
|
export declare const GITHUB_API_URL = "https://api.github.com";
|
|
4
|
+
export declare const GITHUB_REQUEST_TIMEOUT_MS = 15000;
|
|
4
5
|
export interface GithubConnectorStatus {
|
|
5
6
|
provider: "github";
|
|
6
7
|
connected: boolean;
|
|
@@ -14,9 +15,14 @@ export interface GithubApiResponse<T> {
|
|
|
14
15
|
data: T;
|
|
15
16
|
headers: Headers;
|
|
16
17
|
}
|
|
17
|
-
export
|
|
18
|
+
export interface GithubRequestOptions {
|
|
19
|
+
token?: string;
|
|
20
|
+
apiUrl?: string;
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}
|
|
18
23
|
export declare function getGithubConnectorStatus(storage: WandStorage): GithubConnectorStatus;
|
|
19
|
-
export declare function githubRequest<T = unknown>(storage: WandStorage, pathname: string, init?: RequestInit): Promise<GithubApiResponse<T>>;
|
|
20
|
-
export declare function connectGithub(storage: WandStorage, token: string, apiUrl?: string
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
export declare function githubRequest<T = unknown>(storage: WandStorage, pathname: string, init?: RequestInit, options?: GithubRequestOptions): Promise<GithubApiResponse<T>>;
|
|
25
|
+
export declare function connectGithub(storage: WandStorage, token: string, apiUrl?: string, options?: {
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
signal?: AbortSignal;
|
|
28
|
+
}): Promise<GithubConnectorStatus>;
|
package/dist/github-connector.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export const GITHUB_PROVIDER = "github";
|
|
2
2
|
export const GITHUB_API_URL = "https://api.github.com";
|
|
3
|
+
export const GITHUB_REQUEST_TIMEOUT_MS = 15_000;
|
|
4
|
+
let connectEpoch = 0;
|
|
5
|
+
let connectAbort = null;
|
|
3
6
|
function normalizeApiUrl(value) {
|
|
4
7
|
const raw = typeof value === "string" && value.trim() ? value.trim() : GITHUB_API_URL;
|
|
5
8
|
let parsed;
|
|
@@ -17,9 +20,6 @@ function normalizeApiUrl(value) {
|
|
|
17
20
|
}
|
|
18
21
|
return parsed.toString().replace(/\/$/, "");
|
|
19
22
|
}
|
|
20
|
-
export function getGithubApiUrl(storage) {
|
|
21
|
-
return normalizeApiUrl(storage.getConnectorMeta(GITHUB_PROVIDER)?.apiUrl || GITHUB_API_URL);
|
|
22
|
-
}
|
|
23
23
|
export function getGithubConnectorStatus(storage) {
|
|
24
24
|
const meta = storage.getConnectorMeta(GITHUB_PROVIDER);
|
|
25
25
|
const token = storage.getConnectorToken(GITHUB_PROVIDER);
|
|
@@ -45,15 +45,52 @@ function apiPath(pathname) {
|
|
|
45
45
|
}
|
|
46
46
|
return pathname;
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
function requestTimeoutError() {
|
|
49
|
+
const error = new Error("GitHub API 请求超时。");
|
|
50
|
+
error.status = 504;
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
/** Reject when the timeout fires, even if a transport resolves after aborting. */
|
|
54
|
+
function timeoutRejection(timeout) {
|
|
55
|
+
return new Promise((_resolve, reject) => {
|
|
56
|
+
if (timeout.aborted) {
|
|
57
|
+
reject(requestTimeoutError());
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
timeout.addEventListener("abort", () => reject(requestTimeoutError()), { once: true });
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function snapshotConnector(storage) {
|
|
64
|
+
return {
|
|
65
|
+
meta: storage.getConnectorMeta(GITHUB_PROVIDER),
|
|
66
|
+
token: storage.getConnectorToken(GITHUB_PROVIDER),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export async function githubRequest(storage, pathname, init = {}, options) {
|
|
70
|
+
const token = options?.token?.trim() || connectorToken(storage);
|
|
71
|
+
const apiUrl = normalizeApiUrl(options?.apiUrl ?? storage.getConnectorMeta(GITHUB_PROVIDER)?.apiUrl ?? GITHUB_API_URL);
|
|
49
72
|
const headers = new Headers(init.headers);
|
|
50
73
|
headers.set("Accept", "application/vnd.github+json");
|
|
51
74
|
headers.set("X-GitHub-Api-Version", "2022-11-28");
|
|
52
75
|
headers.set("User-Agent", "wand-github-connector");
|
|
53
|
-
headers.set("Authorization", `Bearer ${
|
|
76
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
54
77
|
if (init.body !== undefined && !headers.has("Content-Type"))
|
|
55
78
|
headers.set("Content-Type", "application/json");
|
|
56
|
-
const
|
|
79
|
+
const timeoutMs = options?.timeoutMs ?? GITHUB_REQUEST_TIMEOUT_MS;
|
|
80
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
81
|
+
const signal = init.signal ? AbortSignal.any([init.signal, timeout]) : timeout;
|
|
82
|
+
let response;
|
|
83
|
+
try {
|
|
84
|
+
response = await Promise.race([
|
|
85
|
+
fetch(`${apiUrl}${apiPath(pathname)}`, { ...init, headers, signal }),
|
|
86
|
+
timeoutRejection(timeout),
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (timeout.aborted && !init.signal?.aborted)
|
|
91
|
+
throw requestTimeoutError();
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
57
94
|
const raw = await response.text();
|
|
58
95
|
let data = null;
|
|
59
96
|
try {
|
|
@@ -66,52 +103,87 @@ export async function githubRequest(storage, pathname, init = {}) {
|
|
|
66
103
|
const message = data && typeof data === "object" && "message" in data && typeof data.message === "string"
|
|
67
104
|
? data.message
|
|
68
105
|
: `GitHub API 请求失败(${response.status})。`;
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
throw
|
|
106
|
+
const requestError = new Error(message);
|
|
107
|
+
requestError.status = response.status;
|
|
108
|
+
throw requestError;
|
|
72
109
|
}
|
|
73
110
|
return { data: data, headers: response.headers };
|
|
74
111
|
}
|
|
75
|
-
|
|
112
|
+
function parseScopes(headers) {
|
|
113
|
+
return (headers.get("x-oauth-scopes") || "")
|
|
114
|
+
.split(",")
|
|
115
|
+
.map((scope) => scope.trim())
|
|
116
|
+
.filter(Boolean);
|
|
117
|
+
}
|
|
118
|
+
function restoreConnector(storage, snapshot) {
|
|
119
|
+
if (snapshot.meta?.connectedAt && snapshot.token) {
|
|
120
|
+
storage.saveConnector(GITHUB_PROVIDER, {
|
|
121
|
+
token: snapshot.token,
|
|
122
|
+
apiUrl: snapshot.meta.apiUrl,
|
|
123
|
+
username: snapshot.meta.username,
|
|
124
|
+
markConnected: true,
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
storage.deleteConnector(GITHUB_PROVIDER);
|
|
129
|
+
}
|
|
130
|
+
/** Undo this attempt's own connector write, without clobbering a newer connect. */
|
|
131
|
+
function rollbackOwnConnectWrite(storage, attempt) {
|
|
132
|
+
if (attempt.epoch !== connectEpoch || !attempt.wrote)
|
|
133
|
+
return;
|
|
134
|
+
const current = snapshotConnector(storage);
|
|
135
|
+
const wroteThisAttempt = current.token === attempt.token && current.meta?.apiUrl === attempt.apiUrl;
|
|
136
|
+
if (!wroteThisAttempt)
|
|
137
|
+
return;
|
|
138
|
+
restoreConnector(storage, attempt.previous);
|
|
139
|
+
}
|
|
140
|
+
export async function connectGithub(storage, token, apiUrl, options) {
|
|
76
141
|
const normalizedToken = token.trim();
|
|
77
142
|
if (!normalizedToken)
|
|
78
143
|
throw new Error("GitHub Token 不能为空。");
|
|
79
144
|
const targetApiUrl = normalizeApiUrl(apiUrl);
|
|
80
|
-
const previous = storage
|
|
81
|
-
const
|
|
82
|
-
|
|
145
|
+
const previous = snapshotConnector(storage);
|
|
146
|
+
const epoch = ++connectEpoch;
|
|
147
|
+
connectAbort?.abort();
|
|
148
|
+
const abort = new AbortController();
|
|
149
|
+
connectAbort = abort;
|
|
150
|
+
const signal = options?.signal ? AbortSignal.any([options.signal, abort.signal]) : abort.signal;
|
|
151
|
+
let wrote = false;
|
|
83
152
|
try {
|
|
84
|
-
const profile = await githubRequest(storage, "/user"
|
|
153
|
+
const profile = await githubRequest(storage, "/user", { signal }, {
|
|
154
|
+
token: normalizedToken,
|
|
155
|
+
apiUrl: targetApiUrl,
|
|
156
|
+
timeoutMs: options?.timeoutMs,
|
|
157
|
+
});
|
|
85
158
|
const username = typeof profile.data.login === "string" ? profile.data.login : "";
|
|
86
159
|
if (!username)
|
|
87
160
|
throw new Error("GitHub 返回的账号信息无效。");
|
|
88
|
-
|
|
89
|
-
|
|
161
|
+
if (epoch !== connectEpoch)
|
|
162
|
+
throw new Error("GitHub 连接请求已过期。");
|
|
163
|
+
const scopes = parseScopes(profile.headers);
|
|
90
164
|
const meta = storage.saveConnector(GITHUB_PROVIDER, {
|
|
165
|
+
token: normalizedToken,
|
|
91
166
|
apiUrl: targetApiUrl,
|
|
92
167
|
username,
|
|
93
168
|
markConnected: true,
|
|
94
169
|
});
|
|
170
|
+
wrote = true;
|
|
171
|
+
if (epoch !== connectEpoch)
|
|
172
|
+
throw new Error("GitHub 连接请求已过期。");
|
|
95
173
|
return { ...getGithubConnectorStatus(storage), ...meta, provider: "github", connected: true, scopes };
|
|
96
174
|
}
|
|
97
175
|
catch (error) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
storage.deleteConnector(GITHUB_PROVIDER);
|
|
108
|
-
}
|
|
176
|
+
rollbackOwnConnectWrite(storage, {
|
|
177
|
+
epoch,
|
|
178
|
+
token: normalizedToken,
|
|
179
|
+
apiUrl: targetApiUrl,
|
|
180
|
+
wrote,
|
|
181
|
+
previous,
|
|
182
|
+
});
|
|
109
183
|
throw error;
|
|
110
184
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
export function jsonBody(value) {
|
|
116
|
-
return JSON.stringify(value);
|
|
185
|
+
finally {
|
|
186
|
+
if (connectAbort === abort)
|
|
187
|
+
connectAbort = null;
|
|
188
|
+
}
|
|
117
189
|
}
|
package/dist/missions.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare class Missions {
|
|
|
20
20
|
markInboxRead(sessionId?: string): void;
|
|
21
21
|
diff(missionId: string, attemptId: string): MissionDiff;
|
|
22
22
|
addReviewComment(missionId: string, attemptId: string, input: CreateReviewCommentInput): MissionReviewComment;
|
|
23
|
-
sendReview(missionId: string, attemptId: string, commentIds?: string[]): MissionReviewComment[]
|
|
23
|
+
sendReview(missionId: string, attemptId: string, commentIds?: string[]): Promise<MissionReviewComment[]>;
|
|
24
24
|
resolveReview(missionId: string, attemptId: string, commentIds: string[]): MissionReviewComment[];
|
|
25
25
|
archive(id: string): MissionDetails;
|
|
26
26
|
private dispatchAttempt;
|
package/dist/missions.js
CHANGED
|
@@ -241,7 +241,7 @@ export class Missions {
|
|
|
241
241
|
this.storage.saveMissionReviewComment(comment);
|
|
242
242
|
return comment;
|
|
243
243
|
}
|
|
244
|
-
sendReview(missionId, attemptId, commentIds) {
|
|
244
|
+
async sendReview(missionId, attemptId, commentIds) {
|
|
245
245
|
const attempt = this.requireAttempt(missionId, attemptId);
|
|
246
246
|
if (!attempt.sessionId)
|
|
247
247
|
throw new Error("这个 attempt 没有关联会话。");
|
|
@@ -252,11 +252,24 @@ export class Missions {
|
|
|
252
252
|
const session = this.structured.get(attempt.sessionId);
|
|
253
253
|
if (!session)
|
|
254
254
|
throw new Error("任务会话当前不可用。");
|
|
255
|
-
const
|
|
256
|
-
|
|
255
|
+
const commentIdsToSend = selected.map((comment) => comment.id);
|
|
256
|
+
const accepted = this.structured.sendMessage(attempt.sessionId, reviewPrompt(selected));
|
|
257
|
+
// sendMessage settles after the whole turn. Treat microtask acceptance as
|
|
258
|
+
// "queued or started"; a synchronous/microtask rejection stays pending.
|
|
259
|
+
try {
|
|
260
|
+
await Promise.race([
|
|
261
|
+
accepted,
|
|
262
|
+
new Promise((resolve) => setImmediate(resolve)),
|
|
263
|
+
]);
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
257
266
|
console.error(`[Missions] Review dispatch failed for ${attempt.id}:`, error);
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
accepted.catch((error) => {
|
|
270
|
+
console.error(`[Missions] Review execution failed for ${attempt.id}:`, error);
|
|
258
271
|
});
|
|
259
|
-
this.storage.updateMissionReviewStatus(
|
|
272
|
+
this.storage.updateMissionReviewStatus(commentIdsToSend, "sent");
|
|
260
273
|
this.storage.saveMissionAttempt({ ...attempt, state: "working", updatedAt: nowIso() });
|
|
261
274
|
this.refreshMissionStatus(missionId);
|
|
262
275
|
return this.storage.listMissionReviewComments(missionId, attemptId);
|
|
@@ -129,7 +129,7 @@ export function registerFileRoutes(app, deps) {
|
|
|
129
129
|
size: fileStat.size,
|
|
130
130
|
mime,
|
|
131
131
|
};
|
|
132
|
-
res.json(payload);
|
|
132
|
+
res.json({ ...payload, mtime: fileStat.mtime.toISOString() });
|
|
133
133
|
return;
|
|
134
134
|
}
|
|
135
135
|
if (fileStat.size > MAX_TEXT_PREVIEW_SIZE) {
|
|
@@ -152,7 +152,7 @@ export function registerFileRoutes(app, deps) {
|
|
|
152
152
|
lang: getLanguageFromExt(ext, filePath),
|
|
153
153
|
content,
|
|
154
154
|
};
|
|
155
|
-
res.json(payload);
|
|
155
|
+
res.json({ ...payload, mtime: fileStat.mtime.toISOString() });
|
|
156
156
|
}
|
|
157
157
|
catch (error) {
|
|
158
158
|
res.status(400).json({ error: getErrorMessage(error, "Failed to read file") });
|
|
@@ -196,6 +196,16 @@ export function registerFileRoutes(app, deps) {
|
|
|
196
196
|
res.status(415).json({ error: "仅支持编辑文本类文件。" });
|
|
197
197
|
return;
|
|
198
198
|
}
|
|
199
|
+
if (fileChangedSince(body.expectedMtime, body.expectedSize, fileStat)) {
|
|
200
|
+
res.status(409).json({
|
|
201
|
+
error: "文件已被外部修改。",
|
|
202
|
+
errorCode: "FILE_CHANGED",
|
|
203
|
+
path: resolvedPath,
|
|
204
|
+
size: fileStat.size,
|
|
205
|
+
mtime: fileStat.mtime.toISOString(),
|
|
206
|
+
});
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
199
209
|
const tmpPath = path.join(path.dirname(resolvedPath), `.${baseName}.wand-tmp-${crypto.randomBytes(6).toString("hex")}`);
|
|
200
210
|
try {
|
|
201
211
|
await writeFile(tmpPath, content, { encoding: "utf-8", mode: fileStat.mode & 0o777 });
|
|
@@ -732,6 +742,37 @@ const RAW_MAX_BYTES_BY_KIND = {
|
|
|
732
742
|
audio: 200 * 1024 * 1024,
|
|
733
743
|
binary: 50 * 1024 * 1024,
|
|
734
744
|
};
|
|
745
|
+
/** Compare an optional client baseline (mtime and/or size) against the live file. */
|
|
746
|
+
function fileChangedSince(expectedMtime, expectedSize, actual) {
|
|
747
|
+
const mtime = parseExpectedMtime(expectedMtime);
|
|
748
|
+
if (mtime && !mtimeEquals(mtime, actual.mtime))
|
|
749
|
+
return true;
|
|
750
|
+
const size = parseExpectedSize(expectedSize);
|
|
751
|
+
return size !== undefined && size !== actual.size;
|
|
752
|
+
}
|
|
753
|
+
function parseExpectedMtime(value) {
|
|
754
|
+
if (typeof value === "string")
|
|
755
|
+
return value.trim();
|
|
756
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
757
|
+
return new Date(value).toISOString();
|
|
758
|
+
return "";
|
|
759
|
+
}
|
|
760
|
+
function parseExpectedSize(value) {
|
|
761
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
762
|
+
return value;
|
|
763
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
764
|
+
const parsed = Number(value);
|
|
765
|
+
if (Number.isFinite(parsed))
|
|
766
|
+
return parsed;
|
|
767
|
+
}
|
|
768
|
+
return undefined;
|
|
769
|
+
}
|
|
770
|
+
function mtimeEquals(expected, actual) {
|
|
771
|
+
if (expected === actual.toISOString())
|
|
772
|
+
return true;
|
|
773
|
+
const parsed = Date.parse(expected);
|
|
774
|
+
return Number.isFinite(parsed) && parsed === actual.getTime();
|
|
775
|
+
}
|
|
735
776
|
function classifyFile(ext, baseName) {
|
|
736
777
|
const lowerExt = ext.toLowerCase();
|
|
737
778
|
const lowerBase = baseName.toLowerCase();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { asyncRoute } from "./express-async.js";
|
|
2
|
-
import { connectGithub,
|
|
2
|
+
import { connectGithub, getGithubConnectorStatus, githubRequest, } from "./github-connector.js";
|
|
3
3
|
import { getErrorMessage } from "./error-utils.js";
|
|
4
4
|
function stringQuery(value, fallback = "") {
|
|
5
5
|
return typeof value === "string" ? value.trim() : fallback;
|
|
@@ -8,6 +8,22 @@ function integerQuery(value, fallback, min, max) {
|
|
|
8
8
|
const parsed = Number(stringQuery(value));
|
|
9
9
|
return Number.isInteger(parsed) && parsed >= min && parsed <= max ? parsed : fallback;
|
|
10
10
|
}
|
|
11
|
+
function githubListQuery(reqQuery, defaults = {}) {
|
|
12
|
+
const query = new URLSearchParams();
|
|
13
|
+
if (defaults.state)
|
|
14
|
+
query.set("state", stringQuery(reqQuery.state, defaults.state));
|
|
15
|
+
if (stringQuery(reqQuery.labels))
|
|
16
|
+
query.set("labels", stringQuery(reqQuery.labels));
|
|
17
|
+
const sort = stringQuery(reqQuery.sort, defaults.sort ?? "");
|
|
18
|
+
const direction = stringQuery(reqQuery.direction, defaults.direction ?? "");
|
|
19
|
+
if (sort)
|
|
20
|
+
query.set("sort", sort);
|
|
21
|
+
if (direction)
|
|
22
|
+
query.set("direction", direction);
|
|
23
|
+
query.set("per_page", String(integerQuery(reqQuery.per_page, 30, 1, 100)));
|
|
24
|
+
query.set("page", String(integerQuery(reqQuery.page, 1, 1, 10_000)));
|
|
25
|
+
return query;
|
|
26
|
+
}
|
|
11
27
|
function repoPath(req) {
|
|
12
28
|
const owner = req.params.owner?.trim();
|
|
13
29
|
const repo = req.params.repo?.trim();
|
|
@@ -39,11 +55,19 @@ function publicError(error) {
|
|
|
39
55
|
: 400;
|
|
40
56
|
return { status: status >= 400 && status < 600 ? status : 500, message: getErrorMessage(error, "GitHub 操作失败。") };
|
|
41
57
|
}
|
|
58
|
+
function sendGithubError(res, error, remapAuth = false) {
|
|
59
|
+
const result = publicError(error);
|
|
60
|
+
const status = remapAuth && (result.status === 401 || result.status === 403) ? 400 : result.status;
|
|
61
|
+
res.status(status).json({ error: result.message });
|
|
62
|
+
}
|
|
63
|
+
async function githubJson(storage, pathname, init = {}) {
|
|
64
|
+
return (await githubRequest(storage, pathname, init)).data;
|
|
65
|
+
}
|
|
42
66
|
export function registerGithubRoutes(app, deps) {
|
|
43
67
|
const { storage, requireAdmin, sessions } = deps;
|
|
44
68
|
app.get("/api/connectors/github", requireAdmin, (_req, res) => {
|
|
45
69
|
res.set("Cache-Control", "no-store");
|
|
46
|
-
res.json(
|
|
70
|
+
res.json(getGithubConnectorStatus(storage));
|
|
47
71
|
});
|
|
48
72
|
app.post("/api/connectors/github", requireAdmin, asyncRoute(async (req, res) => {
|
|
49
73
|
try {
|
|
@@ -54,8 +78,7 @@ export function registerGithubRoutes(app, deps) {
|
|
|
54
78
|
res.json(await connectGithub(storage, token, apiUrl));
|
|
55
79
|
}
|
|
56
80
|
catch (error) {
|
|
57
|
-
|
|
58
|
-
res.status(result.status === 401 || result.status === 403 ? 400 : result.status).json({ error: result.message });
|
|
81
|
+
sendGithubError(res, error, true);
|
|
59
82
|
}
|
|
60
83
|
}));
|
|
61
84
|
app.delete("/api/connectors/github", requireAdmin, (_req, res) => {
|
|
@@ -69,8 +92,7 @@ export function registerGithubRoutes(app, deps) {
|
|
|
69
92
|
res.json({ bindings });
|
|
70
93
|
}
|
|
71
94
|
catch (error) {
|
|
72
|
-
|
|
73
|
-
res.status(result.status).json({ error: result.message });
|
|
95
|
+
sendGithubError(res, error);
|
|
74
96
|
}
|
|
75
97
|
});
|
|
76
98
|
app.post("/api/github/repos/:owner/:repo/issues/:number/bindings", requireAdmin, (req, res) => {
|
|
@@ -84,8 +106,7 @@ export function registerGithubRoutes(app, deps) {
|
|
|
84
106
|
res.status(201).json({ ok: true, sessionId });
|
|
85
107
|
}
|
|
86
108
|
catch (error) {
|
|
87
|
-
|
|
88
|
-
res.status(result.status).json({ error: result.message });
|
|
109
|
+
sendGithubError(res, error);
|
|
89
110
|
}
|
|
90
111
|
});
|
|
91
112
|
app.delete("/api/github/repos/:owner/:repo/issues/:number/bindings/:sessionId", requireAdmin, (req, res) => {
|
|
@@ -94,17 +115,15 @@ export function registerGithubRoutes(app, deps) {
|
|
|
94
115
|
res.json({ ok: true });
|
|
95
116
|
}
|
|
96
117
|
catch (error) {
|
|
97
|
-
|
|
98
|
-
res.status(result.status).json({ error: result.message });
|
|
118
|
+
sendGithubError(res, error);
|
|
99
119
|
}
|
|
100
120
|
});
|
|
101
121
|
app.get("/api/github/user", requireAdmin, asyncRoute(async (_req, res) => {
|
|
102
122
|
try {
|
|
103
|
-
res.json(
|
|
123
|
+
res.json(await githubJson(storage, "/user"));
|
|
104
124
|
}
|
|
105
125
|
catch (error) {
|
|
106
|
-
|
|
107
|
-
res.status(result.status).json({ error: result.message });
|
|
126
|
+
sendGithubError(res, error);
|
|
108
127
|
}
|
|
109
128
|
}));
|
|
110
129
|
app.get("/api/github/repos", requireAdmin, asyncRoute(async (req, res) => {
|
|
@@ -124,39 +143,27 @@ export function registerGithubRoutes(app, deps) {
|
|
|
124
143
|
query.set("per_page", String(integerQuery(req.query.per_page, 30, 1, 100)));
|
|
125
144
|
query.set("page", String(integerQuery(req.query.page, 1, 1, 10_000)));
|
|
126
145
|
const suffix = query.toString();
|
|
127
|
-
res.json(
|
|
146
|
+
res.json(await githubJson(storage, `/user/repos?${suffix}`));
|
|
128
147
|
}
|
|
129
148
|
catch (error) {
|
|
130
|
-
|
|
131
|
-
res.status(result.status).json({ error: result.message });
|
|
149
|
+
sendGithubError(res, error);
|
|
132
150
|
}
|
|
133
151
|
}));
|
|
134
152
|
app.get("/api/github/repos/:owner/:repo", requireAdmin, asyncRoute(async (req, res) => {
|
|
135
153
|
try {
|
|
136
|
-
res.json(
|
|
154
|
+
res.json(await githubJson(storage, repoPath(req)));
|
|
137
155
|
}
|
|
138
156
|
catch (error) {
|
|
139
|
-
|
|
140
|
-
res.status(result.status).json({ error: result.message });
|
|
157
|
+
sendGithubError(res, error);
|
|
141
158
|
}
|
|
142
159
|
}));
|
|
143
160
|
app.get("/api/github/repos/:owner/:repo/issues", requireAdmin, asyncRoute(async (req, res) => {
|
|
144
161
|
try {
|
|
145
|
-
const query =
|
|
146
|
-
|
|
147
|
-
if (stringQuery(req.query.labels))
|
|
148
|
-
query.set("labels", stringQuery(req.query.labels));
|
|
149
|
-
if (stringQuery(req.query.sort))
|
|
150
|
-
query.set("sort", stringQuery(req.query.sort));
|
|
151
|
-
if (stringQuery(req.query.direction))
|
|
152
|
-
query.set("direction", stringQuery(req.query.direction));
|
|
153
|
-
query.set("per_page", String(integerQuery(req.query.per_page, 30, 1, 100)));
|
|
154
|
-
query.set("page", String(integerQuery(req.query.page, 1, 1, 10_000)));
|
|
155
|
-
res.json((await githubRequest(storage, `${repoPath(req)}/issues?${query}`)).data);
|
|
162
|
+
const query = githubListQuery(req.query, { state: "open" });
|
|
163
|
+
res.json(await githubJson(storage, `${repoPath(req)}/issues?${query}`));
|
|
156
164
|
}
|
|
157
165
|
catch (error) {
|
|
158
|
-
|
|
159
|
-
res.status(result.status).json({ error: result.message });
|
|
166
|
+
sendGithubError(res, error);
|
|
160
167
|
}
|
|
161
168
|
}));
|
|
162
169
|
app.post("/api/github/repos/:owner/:repo/issues", requireAdmin, asyncRoute(async (req, res) => {
|
|
@@ -167,11 +174,10 @@ export function registerGithubRoutes(app, deps) {
|
|
|
167
174
|
if (body[key] !== undefined)
|
|
168
175
|
payload[key] = body[key];
|
|
169
176
|
}
|
|
170
|
-
res.status(201).json(
|
|
177
|
+
res.status(201).json(await githubJson(storage, `${repoPath(req)}/issues`, { method: "POST", body: JSON.stringify(payload) }));
|
|
171
178
|
}
|
|
172
179
|
catch (error) {
|
|
173
|
-
|
|
174
|
-
res.status(result.status).json({ error: result.message });
|
|
180
|
+
sendGithubError(res, error);
|
|
175
181
|
}
|
|
176
182
|
}));
|
|
177
183
|
app.patch("/api/github/repos/:owner/:repo/issues/:number", requireAdmin, asyncRoute(async (req, res) => {
|
|
@@ -184,37 +190,29 @@ export function registerGithubRoutes(app, deps) {
|
|
|
184
190
|
}
|
|
185
191
|
if (!Object.keys(payload).length)
|
|
186
192
|
throw new Error("没有可更新的 Issue 字段。");
|
|
187
|
-
res.json(
|
|
193
|
+
res.json(await githubJson(storage, `${repoPath(req)}/issues/${issueNumber(req)}`, { method: "PATCH", body: JSON.stringify(payload) }));
|
|
188
194
|
}
|
|
189
195
|
catch (error) {
|
|
190
|
-
|
|
191
|
-
res.status(result.status).json({ error: result.message });
|
|
196
|
+
sendGithubError(res, error);
|
|
192
197
|
}
|
|
193
198
|
}));
|
|
194
199
|
app.post("/api/github/repos/:owner/:repo/issues/:number/comments", requireAdmin, asyncRoute(async (req, res) => {
|
|
195
200
|
try {
|
|
196
201
|
const body = validateObjectBody(req.body);
|
|
197
202
|
const payload = { body: requiredString(body, "body") };
|
|
198
|
-
res.status(201).json(
|
|
203
|
+
res.status(201).json(await githubJson(storage, `${repoPath(req)}/issues/${issueNumber(req)}/comments`, { method: "POST", body: JSON.stringify(payload) }));
|
|
199
204
|
}
|
|
200
205
|
catch (error) {
|
|
201
|
-
|
|
202
|
-
res.status(result.status).json({ error: result.message });
|
|
206
|
+
sendGithubError(res, error);
|
|
203
207
|
}
|
|
204
208
|
}));
|
|
205
209
|
app.get("/api/github/repos/:owner/:repo/pulls", requireAdmin, asyncRoute(async (req, res) => {
|
|
206
210
|
try {
|
|
207
|
-
const query =
|
|
208
|
-
|
|
209
|
-
query.set("sort", stringQuery(req.query.sort, "created"));
|
|
210
|
-
query.set("direction", stringQuery(req.query.direction, "desc"));
|
|
211
|
-
query.set("per_page", String(integerQuery(req.query.per_page, 30, 1, 100)));
|
|
212
|
-
query.set("page", String(integerQuery(req.query.page, 1, 1, 10_000)));
|
|
213
|
-
res.json((await githubRequest(storage, `${repoPath(req)}/pulls?${query}`)).data);
|
|
211
|
+
const query = githubListQuery(req.query, { state: "open", sort: "created", direction: "desc" });
|
|
212
|
+
res.json(await githubJson(storage, `${repoPath(req)}/pulls?${query}`));
|
|
214
213
|
}
|
|
215
214
|
catch (error) {
|
|
216
|
-
|
|
217
|
-
res.status(result.status).json({ error: result.message });
|
|
215
|
+
sendGithubError(res, error);
|
|
218
216
|
}
|
|
219
217
|
}));
|
|
220
218
|
app.post("/api/github/repos/:owner/:repo/pulls", requireAdmin, asyncRoute(async (req, res) => {
|
|
@@ -229,11 +227,10 @@ export function registerGithubRoutes(app, deps) {
|
|
|
229
227
|
if (body[key] !== undefined)
|
|
230
228
|
payload[key] = body[key];
|
|
231
229
|
}
|
|
232
|
-
res.status(201).json(
|
|
230
|
+
res.status(201).json(await githubJson(storage, `${repoPath(req)}/pulls`, { method: "POST", body: JSON.stringify(payload) }));
|
|
233
231
|
}
|
|
234
232
|
catch (error) {
|
|
235
|
-
|
|
236
|
-
res.status(result.status).json({ error: result.message });
|
|
233
|
+
sendGithubError(res, error);
|
|
237
234
|
}
|
|
238
235
|
}));
|
|
239
236
|
}
|