Compare commits

..

7 Commits

Author SHA1 Message Date
jiang 80d93d9b21 chore: update
Agent CI/CD / validate (push) Failing after 1m32s
Agent CI/CD / docker-image (push) Has been skipped
Agent CI/CD / deploy-fallback-log (push) Has been skipped
2026-05-19 10:51:43 +08:00
jiang 0446728f60 chore: add push script to package.json
Agent CI/CD / validate (push) Has been cancelled
Agent CI/CD / docker-image (push) Has been cancelled
Agent CI/CD / deploy-fallback-log (push) Has been cancelled
2026-05-19 10:51:31 +08:00
jiang bb4638a3af chore: use ghproxy to accelerate bun installation
Agent CI/CD / validate (push) Failing after 2m15s
Agent CI/CD / docker-image (push) Has been skipped
Agent CI/CD / deploy-fallback-log (push) Has been skipped
2026-05-19 10:46:28 +08:00
jiang 6a2e09173c chore: use npmmirror registry for bun install in CI
Agent CI/CD / validate (push) Failing after 1m33s
Agent CI/CD / docker-image (push) Has been cancelled
Agent CI/CD / deploy-fallback-log (push) Has been cancelled
2026-05-19 10:37:47 +08:00
jiang f92598803f 新增 opencode 配置文件,移除嵌入式服务器支持
Agent CI/CD / validate (push) Failing after 2m17s
Agent CI/CD / docker-image (push) Has been skipped
Agent CI/CD / deploy-fallback-log (push) Has been skipped
2026-05-18 17:15:42 +08:00
jiang d3f6213187 docs: 更新 README 以匹配当前代码结构和 API
Agent CI/CD / validate (push) Has been cancelled
Agent CI/CD / docker-image (push) Has been cancelled
Agent CI/CD / deploy-fallback-log (push) Has been cancelled
2026-05-18 17:13:45 +08:00
jiang d76d797b0b 更新配置,移除嵌入式 opencode server 支持
Agent CI/CD / docker-image (push) Has been cancelled
Agent CI/CD / deploy-fallback-log (push) Has been cancelled
Agent CI/CD / validate (push) Has been cancelled
2026-05-18 17:12:38 +08:00
11 changed files with 653 additions and 277 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ jobs:
echo "$HOME/.bun/bin" >> "$GITHUB_PATH" echo "$HOME/.bun/bin" >> "$GITHUB_PATH"
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile --registry=https://registry.npmmirror.com
- name: Run checks - name: Run checks
run: bun run check run: bun run check
+1
View File
@@ -0,0 +1 @@
node_modules
+82
View File
@@ -0,0 +1,82 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"dependencies": {
"@opencode-ai/plugin": "1.14.28",
},
"devDependencies": {
"@types/node": "^24.7.2",
"typescript": "^5.9.3",
},
},
},
"packages": {
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
"@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
"@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
"@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.14.28", "", { "dependencies": { "@opencode-ai/sdk": "1.14.28", "effect": "4.0.0-beta.48", "zod": "4.1.8" }, "peerDependencies": { "@opentui/core": ">=0.1.105", "@opentui/solid": ">=0.1.105" }, "optionalPeers": ["@opentui/core", "@opentui/solid"] }, "sha512-cHJo7t1jwrzbkIVmNgggdWh4cyOVGw5fnbSpuYeL6qwfmH3g/6YLWtw5ZYEP6detUkEebT08mHXDGmsMUpQa+A=="],
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.14.28", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-qRFJfD+Zdz3jHHSupW4F6Io1ZFrQ6gCRFlG50O6kEU9xRxrBpK0wGvP+Y5VwwvD/gH9WKMHYinlQpDVI9/lgJQ=="],
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"effect": ["effect@4.0.0-beta.48", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="],
"fast-check": ["fast-check@4.7.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-NsZRtqvSSoCP0HbNjUD+r1JH8zqZalyp6gLY9e7OYs7NK9b6AHOs2baBFeBG7bVNsuoukh89x2Yg3rPsul8ziQ=="],
"find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="],
"ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="],
"msgpackr": ["msgpackr@1.11.10", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-iCZNq+HszvF+fC3anCm4nBmWEnbeIAfpDs6IStAEKhQ2YSgkjzVG2FF9XJqwwQh5bH3N9OUTUt4QwVN6MLMLtA=="],
"msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
"multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="],
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"uuid": ["uuid@13.0.0", "", { "bin": "dist-node/bin/uuid" }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"yaml": ["yaml@2.8.3", "", { "bin": "bin.mjs" }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="],
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
}
}
+413
View File
@@ -0,0 +1,413 @@
{
"name": ".opencode",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"@opencode-ai/plugin": "1.14.28"
},
"devDependencies": {
"@types/node": "^24.7.2",
"typescript": "^5.9.3"
}
},
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
"integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
"integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
"integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
"integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
"integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
"integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@opencode-ai/plugin": {
"version": "1.14.28",
"resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.14.28.tgz",
"integrity": "sha512-cHJo7t1jwrzbkIVmNgggdWh4cyOVGw5fnbSpuYeL6qwfmH3g/6YLWtw5ZYEP6detUkEebT08mHXDGmsMUpQa+A==",
"license": "MIT",
"dependencies": {
"@opencode-ai/sdk": "1.14.28",
"effect": "4.0.0-beta.48",
"zod": "4.1.8"
},
"peerDependencies": {
"@opentui/core": ">=0.1.105",
"@opentui/solid": ">=0.1.105"
},
"peerDependenciesMeta": {
"@opentui/core": {
"optional": true
},
"@opentui/solid": {
"optional": true
}
}
},
"node_modules/@opencode-ai/sdk": {
"version": "1.14.28",
"resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.14.28.tgz",
"integrity": "sha512-qRFJfD+Zdz3jHHSupW4F6Io1ZFrQ6gCRFlG50O6kEU9xRxrBpK0wGvP+Y5VwwvD/gH9WKMHYinlQpDVI9/lgJQ==",
"license": "MIT",
"dependencies": {
"cross-spawn": "7.0.6"
}
},
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.12.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz",
"integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/effect": {
"version": "4.0.0-beta.48",
"resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.48.tgz",
"integrity": "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"fast-check": "^4.6.0",
"find-my-way-ts": "^0.1.6",
"ini": "^6.0.0",
"kubernetes-types": "^1.30.0",
"msgpackr": "^1.11.9",
"multipasta": "^0.2.7",
"toml": "^4.1.1",
"uuid": "^13.0.0",
"yaml": "^2.8.3"
}
},
"node_modules/fast-check": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.7.0.tgz",
"integrity": "sha512-NsZRtqvSSoCP0HbNjUD+r1JH8zqZalyp6gLY9e7OYs7NK9b6AHOs2baBFeBG7bVNsuoukh89x2Yg3rPsul8ziQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
}
],
"license": "MIT",
"dependencies": {
"pure-rand": "^8.0.0"
},
"engines": {
"node": ">=12.17.0"
}
},
"node_modules/find-my-way-ts": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz",
"integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==",
"license": "MIT"
},
"node_modules/ini": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz",
"integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==",
"license": "ISC",
"engines": {
"node": "^20.17.0 || >=22.9.0"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"license": "ISC"
},
"node_modules/kubernetes-types": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz",
"integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==",
"license": "Apache-2.0"
},
"node_modules/msgpackr": {
"version": "1.11.10",
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.10.tgz",
"integrity": "sha512-iCZNq+HszvF+fC3anCm4nBmWEnbeIAfpDs6IStAEKhQ2YSgkjzVG2FF9XJqwwQh5bH3N9OUTUt4QwVN6MLMLtA==",
"license": "MIT",
"optionalDependencies": {
"msgpackr-extract": "^3.0.2"
}
},
"node_modules/msgpackr-extract": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
"integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"node-gyp-build-optional-packages": "5.2.2"
},
"bin": {
"download-msgpackr-prebuilds": "bin/download-prebuilds.js"
},
"optionalDependencies": {
"@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
"@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
"@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
"@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
"@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
"@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
}
},
"node_modules/multipasta": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.7.tgz",
"integrity": "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==",
"license": "MIT"
},
"node_modules/node-gyp-build-optional-packages": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
"integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^2.0.1"
},
"bin": {
"node-gyp-build-optional-packages": "bin.js",
"node-gyp-build-optional-packages-optional": "optional.js",
"node-gyp-build-optional-packages-test": "build-test.js"
}
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/pure-rand": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.0.tgz",
"integrity": "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
}
],
"license": "MIT"
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/toml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/toml/-/toml-4.1.1.tgz",
"integrity": "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw==",
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
},
"node_modules/uuid": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.1.tgz",
"integrity": "sha512-9ezox2roIft6ExBVTVqibSd5dc5/47Sw/uY6b4SjQUT2TzQ0tltNquWA46y4xPQmdZYqvnio22SgWd41M86+jw==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist-node/bin/uuid"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/yaml": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/zod": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz",
"integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"private": true,
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"@opencode-ai/plugin": "1.14.28"
},
"devDependencies": {
"@types/node": "^24.7.2",
"typescript": "^5.9.3"
}
}
+50 -71
View File
@@ -30,41 +30,30 @@ TJWaterAgent/
1. 启动 HTTP 服务。 1. 启动 HTTP 服务。
2. 通过 `@opencode-ai/sdk` 启动内嵌 opencode server,或连接外部 opencode server。 2. 通过 `@opencode-ai/sdk` 启动内嵌 opencode server,或连接外部 opencode server。
3. 管理前端 `session_id -> opencode sessionId` 的映射。 3. 管理前端 `session_id -> opencode sessionId` 的映射。
4. 保存并传递用户 `Authorization``x-user-id``x-project-id``x-trace-id` 4. 保存并传递用户的鉴权信息(accessToken, actorKey)、项目上下文(projectId, projectKey)以及 traceId
5. 把 opencode 输出适配成前端需要的 SSE 事件。 5. 把 opencode 输出适配成前端需要的 SSE 事件。
6.`.opencode/tools/dynamic_http_call.ts` 提供内部回调接口。 6.`.opencode/tools/dynamic_http_call.ts` 等工具提供内部回调接口。
7. 代理调用真实 TJWater 后端 API。 7. 代理调用真实 TJWater 后端 API。
当前 Agent API 的主入口 主要目录结构说明
```text | 目录 | 职责 |
POST /api/v1/agent/chat/stream
```
该接口返回 SSE,事件包括:
| event | 用途 |
| --- | --- | | --- | --- |
| `progress` | 前端过程可视化,展示规划、工具调用和完成状态 | | `src/routes/` | 路由定义,如 `chat.ts` 处理聊天请求 |
| `token` | 最终回答文本流 | | `src/chat/` | 会话桥接逻辑,负责 Express 与 opencode 会话的同步 |
| `tool_call` | 前端地图/面板/图表动作 | | `src/runtime/` | opencode 运行时管理,控制 SDK 的启动与状态检查 |
| `done` | 当前轮完成 | | `src/session/` | 会话注册中心和工具执行时的上下文关联存储 |
| `error` | 当前轮失败 | | `src/tools/` | 内部工具执行器,如 `dynamicHttpExecutor` 负责构造真实的业务请求 |
| `src/learning/` | Agent 学习状态协调器,负责处理记忆累积和知识更新 |
| `src/audit/` | 审计记录,包含学习过程和 LLM 请求审计 |
主要目录和文件 当前 Agent API 的主接口
```text ```text
src/ POST /api/v1/chat/message
server.ts
config.ts
runtime/
session/
chat/
routes/
tools/
``` ```
其中 `src/` 是业务服务层,不直接放 opencode skill 或 agent prompt 该接口返回 SSE 格式数据
## `.opencode/` 的职责 ## `.opencode/` 的职责
@@ -73,40 +62,52 @@ src/
### agents ### agents
```text ```text
.opencode/agents/agent.md .opencode/agents/instruction.md
``` ```
这里定义默认 agent 的角色、行为规则、模型配置和工具使用策略。 这里定义默认 agent 的角色、行为规则、模型配置和工具使用策略。目前的 `default_agent` 配置为 `instruction`
当前项目已将 always-loaded instructions 收敛到 `agent.md``opencode.json` 不再额外配置 `instructions` 数组。
### tools ### tools
```text ```text
.opencode/tools/ .opencode/tools/
dynamic_http_call.ts dynamic_http_call.ts # 动态 HTTP 调用,通过回调 host 服务获取鉴权
locate_features.ts fetch_result_ref.ts # 获取结果引用
view_history.ts locate_features.ts # 地图定位
view_scada.ts memory_manager.ts # 记忆管理
show_chart.ts skill_manager.ts # 技能管理
show_chart.ts # 图表展示
...
``` ```
这些是 opencode 可以调用的自定义工具。 这些是 opencode 可以调用的自定义工具。
`dynamic_http_call.ts` 不直接保存用户 token,也不直接访问后端。它会回调 `TJWaterAgent` 的内部接口,由上级服务层根据当前 session 补上用户 token、项目 ID 和 trace ID,再调用 TJWater 后端。
前端类工具如 `locate_features``view_history``view_scada``show_chart` 主要用于触发 UI 动作或可视化,不应被当作数据查询工具。
### skills ### skills
```text ```text
.opencode/skills/tjwater-skills-root-index/ .opencode/skills/
SKILL.md SKILL.md # 根索引
ai/ analytics/ # 分析类技能(SCADA、水力模拟、爆管分析等)
analytics/ business/ # 业务类技能(模型配置、资产管理、项目环境等)
business/ data/ # 数据类技能(时序数据访问)
data/ platform/ # 平台类技能(审计、缓存、元数据等)
platform/ ```
这里保存 TJWater 技能树,保持树结构以符合渐进式披露设计。
## 启动与开发
项目使用 `bun` 作为包管理器和运行环境。
```bash
# 安装依赖
bun install
# 启动开发环境(带 watch 模式)
bun run dev
# 类型检查
bun run check
``` ```
这里保存 TJWater 技能树,并保持树结构,符合渐进式披露设计。 这里保存 TJWater 技能树,并保持树结构,符合渐进式披露设计。
@@ -150,12 +151,7 @@ typescript
## 启动与部署 ## 启动与部署
支持两种 opencode 接入方式: 默认部署不需要全局安装 `opencode` CLI。服务会通过 `@opencode-ai/sdk` 的 embedded 模式启动 opencode server。
1. Embedded 模式:服务通过 `@opencode-ai/sdk` 调用 `createOpencode`,启动本地 `opencode` CLI 子进程并自动创建 client。
2. Client 模式:服务通过 `createOpencodeClient` 直接连接一个已经存在的 opencode server。
因此,只有 Embedded 模式要求运行环境已安装 `opencode` CLIClient 模式不依赖本地 CLI。
根目录的 Bun scripts 已经封装 `.opencode` 依赖安装和类型检查,日常只需要在 `TJWaterAgent/` 根目录操作。 根目录的 Bun scripts 已经封装 `.opencode` 依赖安装和类型检查,日常只需要在 `TJWaterAgent/` 根目录操作。
@@ -180,21 +176,9 @@ opencode.json
因此修改 agent prompt、tools、skills、模型配置或本地环境变量后,不需要手动重启 `bun run dev` 因此修改 agent prompt、tools、skills、模型配置或本地环境变量后,不需要手动重启 `bun run dev`
本地开发可以在项目根目录的 `.local.env` 中配置环境变量 本地开发可以在项目根目录的 `.local.env` 中配置环境变量
Embedded 模式示例:
```bash ```bash
OPENCODE_MODE=embedded
DEEPSEEK_API_KEY=sk-xxx
TJWATER_API_BASE_URL=http://127.0.0.1:8000
```
Client 模式示例:
```bash
OPENCODE_MODE=client
OPENCODE_CLIENT_BASE_URL=http://127.0.0.1:4096
DEEPSEEK_API_KEY=sk-xxx DEEPSEEK_API_KEY=sk-xxx
TJWATER_API_BASE_URL=http://127.0.0.1:8000 TJWATER_API_BASE_URL=http://127.0.0.1:8000
``` ```
@@ -304,10 +288,5 @@ bun run start
如果需要连接外部独立运行的 opencode server,可以配置: 如果需要连接外部独立运行的 opencode server,可以配置:
```bash ```bash
OPENCODE_MODE=client OPENCODE_BASE_URL=http://127.0.0.1:4096
OPENCODE_CLIENT_BASE_URL=http://127.0.0.1:4096 ```
```
配置后,`TJWaterAgent` 会连接该外部 opencode server,而不是自行启动 embedded opencode server。
兼容说明:历史环境变量 `OPENCODE_BASE_URL` 仍可使用,但建议迁移为 `OPENCODE_CLIENT_BASE_URL`,并显式设置 `OPENCODE_MODE=client`
+6 -12
View File
@@ -13,23 +13,17 @@ services:
NODE_ENV: production NODE_ENV: production
HOST: 0.0.0.0 HOST: 0.0.0.0
PORT: 8787 PORT: 8787
DEEPSEEK_API_KEY: "sk-8941428ad9be4c789becfa8d66534aba" AGENT_INTERNAL_TOKEN: ${AGENT_INTERNAL_TOKEN:-}
TJWATER_API_BASE_URL: "http://127.0.0.1:8000" OPENCODE_BASE_URL: ${OPENCODE_BASE_URL:-http://127.0.0.1:4096}
# Embedded 模式:容器内启动 opencode CLI 子进程 DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}
OPENCODE_MODE: embedded TJWATER_API_BASE_URL: ${TJWATER_API_BASE_URL:-http://127.0.0.1:8000}
OPENCODE_HOSTNAME: 0.0.0.0
OPENCODE_PORT: 4096
# Client 模式:连接外部服务地址,不依赖容器内 CLI
# OPENCODE_MODE: client
# OPENCODE_CLIENT_BASE_URL: "http://host.docker.internal:4096"
volumes: volumes:
- /home/ubuntu/.config/opencode:/root/.config/opencode - /home/ubuntu/.config/opencode:/root/.config/opencode
- /home/ubuntu/.local/share/opencode:/root/.local/share/opencode - /home/ubuntu/.local/share/opencode:/root/.local/share/opencode
- ./opencode/agents:/app/.opencode/agents - ./.opencode/agents:/app/.opencode/agents
- ./opencode/skills:/app/.opencode/skills - ./.opencode/skills:/app/.opencode/skills
- ./logs:/app/logs - ./logs:/app/logs
- ./data:/app/data - ./data:/app/data
ports: ports:
- "8787:8787" - "8787:8787"
# - "4096:4096"
restart: unless-stopped restart: unless-stopped
+1 -1
View File
@@ -8,7 +8,7 @@
"install:opencode": "bun install --cwd .opencode", "install:opencode": "bun install --cwd .opencode",
"typecheck": "tsc --noEmit -p tsconfig.json", "typecheck": "tsc --noEmit -p tsconfig.json",
"typecheck:opencode": "bun run --cwd .opencode typecheck", "typecheck:opencode": "bun run --cwd .opencode typecheck",
"dev": "bun --watch src/server.ts", "dev": "bun run typecheck:opencode && bun --watch src/server.ts",
"build": "bun run check", "build": "bun run check",
"check": "bun run typecheck && bun run typecheck:opencode", "check": "bun run typecheck && bun run typecheck:opencode",
"push": "git add . && git commit -m \"chore: update\" && git push origin main", "push": "git add . && git commit -m \"chore: update\" && git push origin main",
+79 -122
View File
@@ -4,129 +4,86 @@ import { z } from "zod";
// 本地开发可在项目根目录放 .local.env;已存在的系统环境变量优先级更高。 // 本地开发可在项目根目录放 .local.env;已存在的系统环境变量优先级更高。
dotenv.config({ path: ".local.env", override: false }); dotenv.config({ path: ".local.env", override: false });
const optionalString = () =>
z.preprocess(
(value) => {
if (typeof value !== "string") {
return value;
}
const normalized = value.trim();
return normalized.length > 0 ? normalized : undefined;
},
z.string().optional(),
);
// 统一在启动时解析环境变量,避免业务代码里散落字符串默认值。 // 统一在启动时解析环境变量,避免业务代码里散落字符串默认值。
const envSchema = z const envSchema = z.object({
.object({ // 运行环境标识,如 development / production。
// 运行环境标识,如 development / production。 NODE_ENV: z.string().default("development"),
NODE_ENV: z.string().default("development"), // HTTP 服务监听端口。
// HTTP 服务监听端口。 PORT: z.coerce.number().int().positive().default(8787),
PORT: z.coerce.number().int().positive().default(8787), // HTTP 服务监听地址。
// HTTP 服务监听地址。 HOST: z.string().default("0.0.0.0"),
HOST: z.string().default("0.0.0.0"), // Pino 日志级别。
// Pino 日志级别。 LOG_LEVEL: z.string().default("info"),
LOG_LEVEL: z.string().default("info"), // LLM 工具/技能调用审计日志路径。
// LLM 工具/技能调用审计日志路径。 LLM_REQUEST_AUDIT_LOG_PATH: z
LLM_REQUEST_AUDIT_LOG_PATH: z .string()
.string() .default("./logs/llm-request-audit.log"),
.default("./logs/llm-request-audit.log"), // 外部 opencode server 回调本服务内部工具桥时使用的共享鉴权 token;可暂时留空。
// 内部工具桥调用本服务时使用的鉴权 token;未显式配置时启动阶段会自动生成。 AGENT_INTERNAL_TOKEN: z.string().default(""),
AGENT_INTERNAL_TOKEN: optionalString(), // 外部 opencode server 的基础地址。
// opencode 运行模式:embedded 会启动本地 CLI 子进程;client 只连接现有 server。 OPENCODE_BASE_URL: z.string().url(),
OPENCODE_MODE: z.enum(["embedded", "client"]).default("embedded"), // chat session 在本地注册表中的保活时长(秒)。
// embedded opencode server 的监听地址。 SESSION_TTL_SECONDS: z.coerce.number().int().positive().default(1800),
OPENCODE_HOSTNAME: z.string().default("127.0.0.1"), // 提供给本地 opencode tools 读取的会话上下文目录。
// embedded opencode server 的监听端口。 SESSION_CONTEXT_STORAGE_DIR: z.string().default("./data/session-contexts"),
OPENCODE_PORT: z.coerce.number().int().positive().default(4096), // TJWater 后端 API 的基础地址。
// opencode SDK 启动或连接运行时时的超时时间(毫秒)。 TJWATER_API_BASE_URL: z.string().default("http://127.0.0.1:8000"),
OPENCODE_TIMEOUT_MS: z.coerce.number().int().positive().default(5000), // 代理调用 TJWater 后端 API 的超时时间(毫秒)。
// 默认使用的 opencode 模型标识。 TJWATER_API_TIMEOUT_MS: z.coerce.number().int().positive().default(30000),
OPENCODE_MODEL: z.string().default("deepseek/deepseek-v4-pro"), // 后端结果在直接内联返回给模型前允许的最大字节数。
// client 模式下,目标 opencode server 的基础地址。 MAX_INLINE_RESULT_BYTES: z.coerce.number().int().positive().default(12000),
OPENCODE_CLIENT_BASE_URL: z.string().url().optional(), // 生成结果 preview 时最多抽样的条目数。
// chat session 在本地注册表中的保活时长(秒)。 MAX_PREVIEW_SAMPLE_ITEMS: z.coerce.number().int().positive().default(3),
SESSION_TTL_SECONDS: z.coerce.number().int().positive().default(1800), // memory 持久化存储目录。
// 提供给本地 opencode tools 读取的会话上下文目录。 MEMORY_STORAGE_DIR: z.string().default("./data/memory"),
SESSION_CONTEXT_STORAGE_DIR: z.string().default("./data/session-contexts"), // 持久化文件写入前保留历史版本的目录。
// TJWater 后端 API 的基础地址。 PERSISTENCE_HISTORY_DIR: z.string().default("./data/history"),
TJWATER_API_BASE_URL: z.string().default("http://127.0.0.1:8000"), // 注入到 prompt 的 memory 快照最大字符数,避免上下文过大。
// 代理调用 TJWater 后端 API 的超时时间(毫秒)。 MEMORY_MAX_PROMPT_CHARS: z.coerce.number().int().positive().default(1800),
TJWATER_API_TIMEOUT_MS: z.coerce.number().int().positive().default(30000), // session transcript 持久化目录。
// 后端结果在直接内联返回给模型前允许的最大字节数。 SESSION_HISTORY_STORAGE_DIR: z.string().default("./data/session-history"),
MAX_INLINE_RESULT_BYTES: z.coerce.number().int().positive().default(12000), // 每个会话最多保留多少轮 transcript,超过后裁剪旧记录。
// 生成结果 preview 时最多抽样的条目数。 SESSION_HISTORY_MAX_TURNS_PER_SESSION: z.coerce
MAX_PREVIEW_SAMPLE_ITEMS: z.coerce.number().int().positive().default(3), .number()
// memory 持久化存储目录。 .int()
MEMORY_STORAGE_DIR: z.string().default("./data/memory"), .positive()
// 持久化文件写入前保留历史版本的目录。 .default(120),
PERSISTENCE_HISTORY_DIR: z.string().default("./data/history"), // session_search 工具默认返回的最大命中数。
// 注入到 prompt 的 memory 快照最大字符数,避免上下文过大。 SESSION_SEARCH_MAX_RESULTS: z.coerce.number().int().positive().default(8),
MEMORY_MAX_PROMPT_CHARS: z.coerce.number().int().positive().default(1800), // session_search 查询文本最大长度。
// session transcript 持久化目录。 SESSION_SEARCH_MAX_QUERY_CHARS: z.coerce.number().int().positive().default(240),
SESSION_HISTORY_STORAGE_DIR: z.string().default("./data/session-history"), // learning review 会话状态目录。
// 每个会话最多保留多少轮 transcript,超过后裁剪旧记录。 LEARNING_STATE_STORAGE_DIR: z.string().default("./data/learning-state"),
SESSION_HISTORY_MAX_TURNS_PER_SESSION: z.coerce // learning audit 日志路径。
.number() LEARNING_AUDIT_LOG_PATH: z
.int() .string()
.positive() .default("./logs/learning-audit.log"),
.default(120), // learning gate 的最小 turn 冷却间隔;这是运行时节流,不参与内容判断。
// session_search 工具默认返回的最大命中数。 LEARNING_GATE_TURN_COOLDOWN: z.coerce.number().int().positive().default(2),
SESSION_SEARCH_MAX_RESULTS: z.coerce.number().int().positive().default(8), // gate 结果被提升为 review 前的最低置信度。
// session_search 查询文本最大长度。 LEARNING_GATE_MIN_CONFIDENCE: z.coerce.number().min(0).max(1).default(0.65),
SESSION_SEARCH_MAX_QUERY_CHARS: z.coerce.number().int().positive().default(240), // review prompt 最多携带多少轮最近 transcript。
// learning review 会话状态目录。 LEARNING_REVIEW_MAX_RECENT_TURNS: z.coerce.number().int().positive().default(8),
LEARNING_STATE_STORAGE_DIR: z.string().default("./data/learning-state"), // review proposal 的最低置信度阈值。
// learning audit 日志路径。 LEARNING_MIN_PROPOSAL_CONFIDENCE: z.coerce.number().min(0).max(1).default(0.8),
LEARNING_AUDIT_LOG_PATH: z // result_ref 持久化存储目录。
.string() RESULT_REF_STORAGE_DIR: z.string().default("./data/result-refs"),
.default("./logs/learning-audit.log"), // result_ref 保留时长(小时)。
// learning gate 的最小 turn 冷却间隔;这是运行时节流,不参与内容判断。 RESULT_REF_TTL_HOURS: z.coerce.number().int().positive().default(168),
LEARNING_GATE_TURN_COOLDOWN: z.coerce.number().int().positive().default(2), // 定时清理过期 result_ref 的扫描周期(毫秒)。
// gate 结果被提升为 review 前的最低置信度。 RESULT_REF_CLEANUP_INTERVAL_MS: z.coerce
LEARNING_GATE_MIN_CONFIDENCE: z.coerce.number().min(0).max(1).default(0.65), .number()
// review prompt 最多携带多少轮最近 transcript。 .int()
LEARNING_REVIEW_MAX_RECENT_TURNS: z.coerce.number().int().positive().default(8), .positive()
// review proposal 的最低置信度阈值。 .default(3600000),
LEARNING_MIN_PROPOSAL_CONFIDENCE: z.coerce.number().min(0).max(1).default(0.8), // fetch_result_ref 默认最多返回的顶层项/字段数量。
// result_ref 持久化存储目录。 RESULT_REF_MAX_RETRIEVAL_ITEMS: z.coerce
RESULT_REF_STORAGE_DIR: z.string().default("./data/result-refs"), .number()
// result_ref 保留时长(小时)。 .int()
RESULT_REF_TTL_HOURS: z.coerce.number().int().positive().default(168), .positive()
// 定时清理过期 result_ref 的扫描周期(毫秒)。 .default(50),
RESULT_REF_CLEANUP_INTERVAL_MS: z.coerce });
.number()
.int()
.positive()
.default(3600000),
// fetch_result_ref 默认最多返回的顶层项/字段数量。
RESULT_REF_MAX_RETRIEVAL_ITEMS: z.coerce
.number()
.int()
.positive()
.default(50),
})
.superRefine((env, ctx) => {
if (env.OPENCODE_MODE === "client" && !env.OPENCODE_CLIENT_BASE_URL) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["OPENCODE_CLIENT_BASE_URL"],
message: "OPENCODE_CLIENT_BASE_URL is required when OPENCODE_MODE=client",
});
}
});
export type AppConfig = z.infer<typeof envSchema>; export type AppConfig = z.infer<typeof envSchema>;
const normalizedEnv = { export const config: AppConfig = envSchema.parse(process.env);
...process.env,
OPENCODE_MODE:
process.env.OPENCODE_MODE ??
(process.env.OPENCODE_CLIENT_BASE_URL || process.env.OPENCODE_BASE_URL
? "client"
: "embedded"),
OPENCODE_CLIENT_BASE_URL:
process.env.OPENCODE_CLIENT_BASE_URL ?? process.env.OPENCODE_BASE_URL,
};
export const config: AppConfig = envSchema.parse(normalizedEnv);
+5 -64
View File
@@ -1,5 +1,4 @@
import { import {
createOpencode,
createOpencodeClient, createOpencodeClient,
type OpencodeClient, type OpencodeClient,
} from "@opencode-ai/sdk/v2"; } from "@opencode-ai/sdk/v2";
@@ -19,7 +18,6 @@ type RuntimeModelOverride = {
export class OpencodeRuntimeAdapter { export class OpencodeRuntimeAdapter {
private clientPromise: Promise<OpencodeClient> | null = null; private clientPromise: Promise<OpencodeClient> | null = null;
private closeServer: (() => void) | null = null;
async ensureClient(): Promise<OpencodeClient> { async ensureClient(): Promise<OpencodeClient> {
if (!this.clientPromise) { if (!this.clientPromise) {
@@ -99,81 +97,24 @@ export class OpencodeRuntimeAdapter {
} }
async dispose(): Promise<void> { async dispose(): Promise<void> {
this.closeServer?.();
this.closeServer = null;
this.clientPromise = null; this.clientPromise = null;
} }
private async bootstrapClient(): Promise<OpencodeClient> { private async bootstrapClient(): Promise<OpencodeClient> {
if (config.OPENCODE_MODE === "client") {
logger.info(
{
baseUrl: config.OPENCODE_CLIENT_BASE_URL,
mode: config.OPENCODE_MODE,
},
"connecting to opencode server in client mode",
);
return createOpencodeClient({
baseUrl: config.OPENCODE_CLIENT_BASE_URL,
});
}
// embedded 模式下,把服务内工具桥地址注入到 opencode 进程环境里,
// 这样 .opencode/tools 下的自定义工具可以回调本服务。
process.env.TJWATER_AGENT_INTERNAL_BASE_URL = `http://127.0.0.1:${config.PORT}`;
process.env.TJWATER_AGENT_INTERNAL_TOKEN =
config.AGENT_INTERNAL_TOKEN ??
process.env.TJWATER_AGENT_INTERNAL_TOKEN ??
"";
logger.info( logger.info(
{ {
hostname: config.OPENCODE_HOSTNAME, baseUrl: config.OPENCODE_BASE_URL,
port: config.OPENCODE_PORT,
model: config.OPENCODE_MODEL,
mode: config.OPENCODE_MODE,
}, },
"starting opencode server in embedded mode", "connecting to opencode server",
); );
return createOpencodeClient({
let runtime; baseUrl: config.OPENCODE_BASE_URL,
try { });
runtime = await createOpencode({
hostname: config.OPENCODE_HOSTNAME,
port: config.OPENCODE_PORT,
timeout: config.OPENCODE_TIMEOUT_MS,
config: {
model: config.OPENCODE_MODEL,
},
});
} catch (error) {
if (isMissingOpencodeCli(error)) {
throw new Error(
"embedded mode requires the opencode CLI to be installed and available in PATH; otherwise set OPENCODE_MODE=client and provide OPENCODE_CLIENT_BASE_URL",
);
}
throw error;
}
this.closeServer = () => {
runtime.server.close();
};
return runtime.client;
} }
} }
export const opencodeRuntime = new OpencodeRuntimeAdapter(); export const opencodeRuntime = new OpencodeRuntimeAdapter();
function isMissingOpencodeCli(error: unknown): error is NodeJS.ErrnoException {
return (
typeof error === "object" &&
error !== null &&
"code" in error &&
(error as NodeJS.ErrnoException).code === "ENOENT"
);
}
function requireData<T>(data: T | undefined, operation: string): T { function requireData<T>(data: T | undefined, operation: string): T {
if (data === undefined) { if (data === undefined) {
throw new Error(`${operation} returned no data`); throw new Error(`${operation} returned no data`);
+1 -6
View File
@@ -1,4 +1,3 @@
import { randomUUID } from "node:crypto";
import cors from "cors"; import cors from "cors";
import express from "express"; import express from "express";
@@ -28,10 +27,7 @@ const learningOrchestrator = new LearningOrchestrator(
); );
const resultReferenceStore = new ResultReferenceStore(); const resultReferenceStore = new ResultReferenceStore();
const dynamicHttpExecutor = new DynamicHttpExecutor(resultReferenceStore); const dynamicHttpExecutor = new DynamicHttpExecutor(resultReferenceStore);
const internalToken = config.AGENT_INTERNAL_TOKEN ?? randomUUID(); const internalToken = config.AGENT_INTERNAL_TOKEN;
// 这个 token 只用于仍需服务端上下文的工具桥(dynamic_http_call / fetch_result_ref)。
process.env.TJWATER_AGENT_INTERNAL_TOKEN = internalToken;
app.use(cors()); app.use(cors());
app.use(express.json({ limit: "1mb" })); app.use(express.json({ limit: "1mb" }));
@@ -205,7 +201,6 @@ const shutdown = async () => {
logger.info("shutting down TJWaterAgent"); logger.info("shutting down TJWaterAgent");
server.close(); server.close();
resultReferenceStore.stopCleanupLoop(); resultReferenceStore.stopCleanupLoop();
// 同步关闭 embedded opencode server,避免本服务退出后留下孤儿进程。
await opencodeRuntime.dispose(); await opencodeRuntime.dispose();
}; };