@agent-spaces/server 0.3.64 → 0.3.65
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/adapters/claude-code-runtime/index.js +68 -2
- package/dist/adapters/claude-code-runtime/message-format.js +26 -0
- package/dist/adapters/claude-code-runtime/sdk-config.js +12 -0
- package/dist/adapters/git.js +13 -6
- package/dist/agents/agent-message-parts.js +1 -1
- package/dist/agents/commit-agent.js +4 -2
- package/dist/agents/issue-task-controller.js +6 -2
- package/dist/app.js +6 -68
- package/dist/routes/agent-sse.js +1 -0
- package/dist/routes/agent.js +3 -0
- package/dist/routes/database.js +1 -0
- package/dist/routes/git.js +197 -317
- package/dist/routes/robot-account.js +68 -0
- package/dist/services/agent.js +36 -2
- package/dist/services/builtin-tools/kanban-tools.js +4 -0
- package/dist/services/git-operation-log.js +40 -0
- package/dist/services/global-wechat-qr.js +72 -0
- package/dist/services/hook-engine.js +175 -17
- package/dist/services/notification-hub/bot-agent.js +3 -1
- package/dist/services/notification-hub/service.js +12 -2
- package/dist/services/robot-account.js +34 -0
- package/dist/services/workspace.js +2 -0
- package/dist/storage/robot-account-store.js +39 -0
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +4 -4
- package/dist/web/__next._full.txt +23 -25
- package/dist/web/__next._head.txt +4 -4
- package/dist/web/__next._index.txt +13 -13
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/chunks/{04a4vsq2wknm4.js → 0-a.-hl3wv99k.js} +1 -1
- package/dist/web/_next/static/chunks/{0ctg5qmw7uu.1.js → 0-pi.67w.ymsr.js} +3 -3
- package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +1 -0
- package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +1 -0
- package/dist/web/_next/static/chunks/0.6b64sy0g5.3.js +3 -0
- package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +1 -0
- package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +1 -0
- package/dist/web/_next/static/chunks/02802taez976w.js +67 -0
- package/dist/web/_next/static/chunks/02j4sr9psucf-.js +1 -0
- package/dist/web/_next/static/chunks/033.awk90g6eh.js +1 -0
- package/dist/web/_next/static/chunks/06.kdsa9rr2h1.js +1 -0
- package/dist/web/_next/static/chunks/06si8l0doqe8p.js +1 -0
- package/dist/web/_next/static/chunks/08ugbvzr20drp.js +2 -0
- package/dist/web/_next/static/chunks/{0n6_4so_afuwk.js → 08x85faoz58i7.js} +2 -2
- package/dist/web/_next/static/chunks/{0v3a_x4vt42kx.js → 0_cqojbge48kq.js} +1 -1
- package/dist/web/_next/static/chunks/{0a5y-vhf4i5id.js → 0d6~~lv0-6kex.js} +1 -1
- package/dist/web/_next/static/chunks/{0vbqaynjq8ga..js → 0db14450oqzhg.js} +1 -1
- package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +1 -0
- package/dist/web/_next/static/chunks/0j-l7l_k9jt45.js +1 -0
- package/dist/web/_next/static/chunks/0kbvoxla7f751.js +1 -0
- package/dist/web/_next/static/chunks/{1442stwa0gbxe.js → 0l2w0qz1ea7ql.js} +2 -2
- package/dist/web/_next/static/chunks/0p3h-h3zj5ujg.js +30 -0
- package/dist/web/_next/static/chunks/0paaat8y96ayc.js +7 -0
- package/dist/web/_next/static/chunks/0pm-un~-sy3rl.js +1 -0
- package/dist/web/_next/static/chunks/0pq318f8xgafg.js +1 -0
- package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +1 -0
- package/dist/web/_next/static/chunks/0u6h-hqc_i52e.js +1 -0
- package/dist/web/_next/static/chunks/0uxzi~9b~k4mn.js +31 -0
- package/dist/web/_next/static/chunks/{08ckpwmmwlors.js → 0vis~lx0nxgn-.js} +2 -2
- package/dist/web/_next/static/chunks/{10l-9myrdo3x9.js → 0w7.tb29958zj.js} +1 -1
- package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +3 -0
- package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +1 -0
- package/dist/web/_next/static/chunks/0zncg76rgtbm-.js +1 -0
- package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +1 -0
- package/dist/web/_next/static/chunks/{0iuf30t0rjsjv.js → 11cchuwoupmbf.js} +1 -1
- package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +1 -0
- package/dist/web/_next/static/chunks/1452eibz62gs6.css +1 -0
- package/dist/web/_next/static/chunks/16zlreiems5cn.js +1 -0
- package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +1 -0
- package/dist/web/_next/static/chunks/18c7g84y01mj0.js +1 -0
- package/dist/web/_next/static/chunks/{turbopack-0ndm2zc_pdptk.js → turbopack-11qjwlynmfc-n.js} +1 -1
- package/dist/web/_not-found/__next._full.txt +30 -29
- package/dist/web/_not-found/__next._head.txt +4 -4
- package/dist/web/_not-found/__next._index.txt +13 -13
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dist/web/_not-found/__next._not-found.txt +3 -3
- package/dist/web/_not-found/__next._tree.txt +2 -2
- package/dist/web/_not-found.html +1 -1
- package/dist/web/_not-found.txt +30 -29
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +23 -25
- package/dist/web/login/__next._full.txt +28 -28
- package/dist/web/login/__next._head.txt +4 -4
- package/dist/web/login/__next._index.txt +13 -13
- package/dist/web/login/__next._tree.txt +2 -2
- package/dist/web/login/__next.login.__PAGE__.txt +4 -4
- package/dist/web/login/__next.login.txt +3 -3
- package/dist/web/login.html +1 -1
- package/dist/web/login.txt +28 -28
- package/dist/web/settings/__next._full.txt +33 -32
- package/dist/web/settings/__next._head.txt +4 -4
- package/dist/web/settings/__next._index.txt +13 -13
- package/dist/web/settings/__next._tree.txt +2 -2
- package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
- package/dist/web/settings/__next.settings.txt +5 -5
- package/dist/web/settings/agents/__next._full.txt +24 -24
- package/dist/web/settings/agents/__next._head.txt +4 -4
- package/dist/web/settings/agents/__next._index.txt +13 -13
- package/dist/web/settings/agents/__next._tree.txt +2 -2
- package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
- package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
- package/dist/web/settings/agents/__next.settings.txt +5 -5
- package/dist/web/settings/agents.html +1 -1
- package/dist/web/settings/agents.txt +24 -24
- package/dist/web/settings/mcps/__next._full.txt +24 -24
- package/dist/web/settings/mcps/__next._head.txt +4 -4
- package/dist/web/settings/mcps/__next._index.txt +13 -13
- package/dist/web/settings/mcps/__next._tree.txt +2 -2
- package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
- package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
- package/dist/web/settings/mcps/__next.settings.txt +5 -5
- package/dist/web/settings/mcps.html +1 -1
- package/dist/web/settings/mcps.txt +24 -24
- package/dist/web/settings/models/__next._full.txt +24 -24
- package/dist/web/settings/models/__next._head.txt +4 -4
- package/dist/web/settings/models/__next._index.txt +13 -13
- package/dist/web/settings/models/__next._tree.txt +2 -2
- package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
- package/dist/web/settings/models/__next.settings.models.txt +3 -3
- package/dist/web/settings/models/__next.settings.txt +5 -5
- package/dist/web/settings/models.html +1 -1
- package/dist/web/settings/models.txt +24 -24
- package/dist/web/settings/output-styles/__next._full.txt +24 -24
- package/dist/web/settings/output-styles/__next._head.txt +4 -4
- package/dist/web/settings/output-styles/__next._index.txt +13 -13
- package/dist/web/settings/output-styles/__next._tree.txt +2 -2
- package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
- package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
- package/dist/web/settings/output-styles/__next.settings.txt +5 -5
- package/dist/web/settings/output-styles.html +1 -1
- package/dist/web/settings/output-styles.txt +24 -24
- package/dist/web/settings/prompts/__next._full.txt +24 -24
- package/dist/web/settings/prompts/__next._head.txt +4 -4
- package/dist/web/settings/prompts/__next._index.txt +13 -13
- package/dist/web/settings/prompts/__next._tree.txt +2 -2
- package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
- package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
- package/dist/web/settings/prompts/__next.settings.txt +5 -5
- package/dist/web/settings/prompts.html +1 -1
- package/dist/web/settings/prompts.txt +24 -24
- package/dist/web/settings/providers/__next._full.txt +24 -24
- package/dist/web/settings/providers/__next._head.txt +4 -4
- package/dist/web/settings/providers/__next._index.txt +13 -13
- package/dist/web/settings/providers/__next._tree.txt +2 -2
- package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
- package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
- package/dist/web/settings/providers/__next.settings.txt +5 -5
- package/dist/web/settings/providers.html +1 -1
- package/dist/web/settings/providers.txt +24 -24
- package/dist/web/settings/skills/__next._full.txt +24 -24
- package/dist/web/settings/skills/__next._head.txt +4 -4
- package/dist/web/settings/skills/__next._index.txt +13 -13
- package/dist/web/settings/skills/__next._tree.txt +2 -2
- package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
- package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
- package/dist/web/settings/skills/__next.settings.txt +5 -5
- package/dist/web/settings/skills.html +1 -1
- package/dist/web/settings/skills.txt +24 -24
- package/dist/web/settings/tools/__next._full.txt +24 -24
- package/dist/web/settings/tools/__next._head.txt +4 -4
- package/dist/web/settings/tools/__next._index.txt +13 -13
- package/dist/web/settings/tools/__next._tree.txt +2 -2
- package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
- package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
- package/dist/web/settings/tools/__next.settings.txt +5 -5
- package/dist/web/settings/tools.html +1 -1
- package/dist/web/settings/tools.txt +24 -24
- package/dist/web/settings.html +1 -1
- package/dist/web/settings.txt +33 -32
- package/dist/web/workflows/__next._full.txt +31 -33
- package/dist/web/workflows/__next._head.txt +4 -4
- package/dist/web/workflows/__next._index.txt +13 -13
- package/dist/web/workflows/__next._tree.txt +2 -2
- package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
- package/dist/web/workflows/__next.workflows.txt +3 -3
- package/dist/web/workflows.html +1 -1
- package/dist/web/workflows.txt +31 -33
- package/dist/web/workspace/_/__next._full.txt +28 -29
- package/dist/web/workspace/_/__next._head.txt +4 -4
- package/dist/web/workspace/_/__next._index.txt +13 -13
- package/dist/web/workspace/_/__next._tree.txt +2 -2
- package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.txt +3 -3
- package/dist/web/workspace/_.html +1 -1
- package/dist/web/workspace/_.txt +28 -29
- package/dist/web/workspaces/__next._full.txt +29 -31
- package/dist/web/workspaces/__next._head.txt +4 -4
- package/dist/web/workspaces/__next._index.txt +13 -13
- package/dist/web/workspaces/__next._tree.txt +2 -2
- package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
- package/dist/web/workspaces/__next.workspaces.txt +3 -3
- package/dist/web/workspaces.html +1 -1
- package/dist/web/workspaces.txt +29 -31
- package/dist/ws/agent-runner.js +82 -6
- package/package.json +10 -11
- package/dist/web/_next/static/chunks/0-9194nbxy_6d.js +0 -1
- package/dist/web/_next/static/chunks/02mf61htij8g0.js +0 -31
- package/dist/web/_next/static/chunks/04dg4l4hv11q2.js +0 -3
- package/dist/web/_next/static/chunks/04fxddw-y20~-.js +0 -7
- package/dist/web/_next/static/chunks/04w7~5ooz_pwm.js +0 -1
- package/dist/web/_next/static/chunks/05oc21sj24wtq.js +0 -1
- package/dist/web/_next/static/chunks/08k~meqbd8d6v.js +0 -1
- package/dist/web/_next/static/chunks/099h96wjsdb25.js +0 -2
- package/dist/web/_next/static/chunks/09h23logvq65m.js +0 -1
- package/dist/web/_next/static/chunks/0ake2f3x2uan4.js +0 -24
- package/dist/web/_next/static/chunks/0ck8dw_9-1cdx.js +0 -1
- package/dist/web/_next/static/chunks/0ewceguykiut5.js +0 -1
- package/dist/web/_next/static/chunks/0f7cb9li~3jra.js +0 -67
- package/dist/web/_next/static/chunks/0ghs9on7wodrm.js +0 -1
- package/dist/web/_next/static/chunks/0h9cu-k6z5o08.js +0 -1
- package/dist/web/_next/static/chunks/0jb4~3qqkw2ps.js +0 -1
- package/dist/web/_next/static/chunks/0nb8.iteznuvx.js +0 -1
- package/dist/web/_next/static/chunks/0os58q93stt7j.js +0 -3
- package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +0 -1
- package/dist/web/_next/static/chunks/0s9lmk8iauou3.js +0 -1
- package/dist/web/_next/static/chunks/0sp_zbx~w0am7.js +0 -1
- package/dist/web/_next/static/chunks/0tel~zsv4z8l_.js +0 -1
- package/dist/web/_next/static/chunks/0twamv4wr_dbv.js +0 -1
- package/dist/web/_next/static/chunks/0uhol5wafdbl~.js +0 -1
- package/dist/web/_next/static/chunks/0v7q4mys.mnm..css +0 -1
- package/dist/web/_next/static/chunks/0~3xm80~j9ddg.js +0 -1
- package/dist/web/_next/static/chunks/14s37n6h0_q3x.js +0 -16
- package/dist/web/_next/static/chunks/161ik5kfyuwtd.js +0 -1
- package/dist/web/_next/static/chunks/17k~zbv0_4rep.js +0 -1
- package/public/output-styles/carmack-mode.md +0 -381
- package/public/output-styles/codex-rigor-mode.md +0 -114
- package/public/output-styles/dhh-mode.md +0 -265
- package/public/output-styles/evan-you-mode.md +0 -539
- package/public/output-styles/index.json +0 -37
- package/public/output-styles/jobs-mode.md +0 -369
- package/public/output-styles/linus-mode.md +0 -135
- package/public/output-styles/uncle-bob-mode.md +0 -221
- package/public/prompt/andrej-karpathy-skills.md +0 -65
- package/public/prompt/claude-token-efficient-coding.md +0 -35
- package/public/prompt/index.json +0 -12
- package/public/skills/index.json +0 -92
- package/public/skills/planning-with-files/planning-with-files-zh/SKILL.md +0 -243
- package/public/skills/planning-with-files/planning-with-files-zh/scripts/check-complete.ps1 +0 -44
- package/public/skills/planning-with-files/planning-with-files-zh/scripts/check-complete.sh +0 -46
- package/public/skills/planning-with-files/planning-with-files-zh/scripts/init-session.ps1 +0 -124
- package/public/skills/planning-with-files/planning-with-files-zh/scripts/init-session.sh +0 -124
- package/public/skills/planning-with-files/planning-with-files-zh/scripts/session-catchup.py +0 -438
- package/public/skills/planning-with-files/planning-with-files-zh/templates/findings.md +0 -29
- package/public/skills/planning-with-files/planning-with-files-zh/templates/progress.md +0 -40
- package/public/skills/planning-with-files/planning-with-files-zh/templates/task_plan.md +0 -58
- package/public/skills/superpowers/brainstorming/SKILL.md +0 -164
- package/public/skills/superpowers/brainstorming/scripts/frame-template.html +0 -214
- package/public/skills/superpowers/brainstorming/scripts/helper.js +0 -88
- package/public/skills/superpowers/brainstorming/scripts/server.cjs +0 -354
- package/public/skills/superpowers/brainstorming/scripts/start-server.sh +0 -148
- package/public/skills/superpowers/brainstorming/scripts/stop-server.sh +0 -56
- package/public/skills/superpowers/brainstorming/spec-document-reviewer-prompt.md +0 -49
- package/public/skills/superpowers/brainstorming/visual-companion.md +0 -287
- package/public/skills/superpowers/dispatching-parallel-agents/SKILL.md +0 -182
- package/public/skills/superpowers/executing-plans/SKILL.md +0 -70
- package/public/skills/superpowers/finishing-a-development-branch/SKILL.md +0 -251
- package/public/skills/superpowers/receiving-code-review/SKILL.md +0 -213
- package/public/skills/superpowers/requesting-code-review/SKILL.md +0 -103
- package/public/skills/superpowers/requesting-code-review/code-reviewer.md +0 -168
- package/public/skills/superpowers/subagent-driven-development/SKILL.md +0 -279
- package/public/skills/superpowers/subagent-driven-development/code-quality-reviewer-prompt.md +0 -25
- package/public/skills/superpowers/subagent-driven-development/implementer-prompt.md +0 -113
- package/public/skills/superpowers/subagent-driven-development/spec-reviewer-prompt.md +0 -61
- package/public/skills/superpowers/systematic-debugging/CREATION-LOG.md +0 -119
- package/public/skills/superpowers/systematic-debugging/SKILL.md +0 -296
- package/public/skills/superpowers/systematic-debugging/condition-based-waiting-example.ts +0 -158
- package/public/skills/superpowers/systematic-debugging/condition-based-waiting.md +0 -115
- package/public/skills/superpowers/systematic-debugging/defense-in-depth.md +0 -122
- package/public/skills/superpowers/systematic-debugging/find-polluter.sh +0 -63
- package/public/skills/superpowers/systematic-debugging/root-cause-tracing.md +0 -169
- package/public/skills/superpowers/systematic-debugging/test-academic.md +0 -14
- package/public/skills/superpowers/systematic-debugging/test-pressure-1.md +0 -58
- package/public/skills/superpowers/systematic-debugging/test-pressure-2.md +0 -68
- package/public/skills/superpowers/systematic-debugging/test-pressure-3.md +0 -69
- package/public/skills/superpowers/test-driven-development/SKILL.md +0 -371
- package/public/skills/superpowers/test-driven-development/testing-anti-patterns.md +0 -299
- package/public/skills/superpowers/using-git-worktrees/SKILL.md +0 -215
- package/public/skills/superpowers/using-superpowers/SKILL.md +0 -117
- package/public/skills/superpowers/using-superpowers/references/codex-tools.md +0 -59
- package/public/skills/superpowers/using-superpowers/references/copilot-tools.md +0 -42
- package/public/skills/superpowers/using-superpowers/references/gemini-tools.md +0 -51
- package/public/skills/superpowers/verification-before-completion/SKILL.md +0 -139
- package/public/skills/superpowers/writing-plans/SKILL.md +0 -152
- package/public/skills/superpowers/writing-plans/plan-document-reviewer-prompt.md +0 -49
- package/public/skills/superpowers/writing-skills/SKILL.md +0 -655
- package/public/skills/superpowers/writing-skills/anthropic-best-practices.md +0 -1150
- package/public/skills/superpowers/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
- package/public/skills/superpowers/writing-skills/graphviz-conventions.dot +0 -172
- package/public/skills/superpowers/writing-skills/persuasion-principles.md +0 -187
- package/public/skills/superpowers/writing-skills/render-graphs.js +0 -168
- package/public/skills/superpowers/writing-skills/testing-skills-with-subagents.md +0 -384
- /package/dist/web/_next/static/{e8w3vd9Eov_LDYWj_0zsE → x4-CsKe6qUQyFDWVtfvUg}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{e8w3vd9Eov_LDYWj_0zsE → x4-CsKe6qUQyFDWVtfvUg}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web/_next/static/{e8w3vd9Eov_LDYWj_0zsE → x4-CsKe6qUQyFDWVtfvUg}/_ssgManifest.js +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BUILT_IN_AGENT_TOOLS } from '@agent-spaces/shared';
|
|
2
2
|
import * as kanbanStore from '../../storage/kanban-store.js';
|
|
3
|
+
import { broadcastToWorkspace } from '../../ws/connection-manager.js';
|
|
3
4
|
import { assertRecord, readOptionalString, readRequiredString, readString } from './input-helpers.js';
|
|
4
5
|
const kanbanLayoutModes = ['horizontal', 'vertical'];
|
|
5
6
|
const kanbanPriorities = ['low', 'medium', 'high'];
|
|
@@ -158,6 +159,7 @@ function createKanbanBoard(workspaceId, input) {
|
|
|
158
159
|
const created = kanbanStore.getBoard(workspaceId);
|
|
159
160
|
if (!created)
|
|
160
161
|
throw new Error('Failed to create Kanban board.');
|
|
162
|
+
broadcastToWorkspace(workspaceId, 'kanban.updated', created);
|
|
161
163
|
return created;
|
|
162
164
|
}
|
|
163
165
|
function updateKanbanBoard(workspaceId, input) {
|
|
@@ -185,6 +187,7 @@ function updateKanbanBoard(workspaceId, input) {
|
|
|
185
187
|
const updated = kanbanStore.listBoards(workspaceId).find((item) => item.id === board.id);
|
|
186
188
|
if (!updated)
|
|
187
189
|
throw new Error(`Kanban board not found: ${board.id}`);
|
|
190
|
+
broadcastToWorkspace(workspaceId, 'kanban.updated', updated);
|
|
188
191
|
return updated;
|
|
189
192
|
}
|
|
190
193
|
function deleteKanbanBoard(workspaceId, input) {
|
|
@@ -193,6 +196,7 @@ function deleteKanbanBoard(workspaceId, input) {
|
|
|
193
196
|
const ok = kanbanStore.deleteBoard(workspaceId, board.id);
|
|
194
197
|
if (!ok)
|
|
195
198
|
throw new Error(`Kanban board not found: ${board.id}`);
|
|
199
|
+
broadcastToWorkspace(workspaceId, 'kanban.deleted', { boardId: board.id });
|
|
196
200
|
return { ok: true, deletedBoardId: board.id };
|
|
197
201
|
}
|
|
198
202
|
function resolveKanbanBoard(workspaceId, boardId) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const MAX_ENTRIES = 1000;
|
|
2
|
+
const store = new Map();
|
|
3
|
+
function getList(workspaceId) {
|
|
4
|
+
let list = store.get(workspaceId);
|
|
5
|
+
if (!list) {
|
|
6
|
+
list = [];
|
|
7
|
+
store.set(workspaceId, list);
|
|
8
|
+
}
|
|
9
|
+
return list;
|
|
10
|
+
}
|
|
11
|
+
let counter = 0;
|
|
12
|
+
export function logGitOperation(workspaceId, operation, input, result, error, duration = 0) {
|
|
13
|
+
const list = getList(workspaceId);
|
|
14
|
+
const entry = {
|
|
15
|
+
id: `${Date.now()}-${++counter}`,
|
|
16
|
+
operation,
|
|
17
|
+
input,
|
|
18
|
+
output: truncateOutput(result),
|
|
19
|
+
error,
|
|
20
|
+
timestamp: new Date().toISOString(),
|
|
21
|
+
duration,
|
|
22
|
+
};
|
|
23
|
+
list.unshift(entry);
|
|
24
|
+
if (list.length > MAX_ENTRIES)
|
|
25
|
+
list.length = MAX_ENTRIES;
|
|
26
|
+
}
|
|
27
|
+
export function getGitOperations(workspaceId) {
|
|
28
|
+
return getList(workspaceId);
|
|
29
|
+
}
|
|
30
|
+
function truncateOutput(value) {
|
|
31
|
+
if (value === undefined || value === null)
|
|
32
|
+
return value;
|
|
33
|
+
if (typeof value === 'string')
|
|
34
|
+
return value.length > 5000 ? value.slice(0, 5000) + '...(truncated)' : value;
|
|
35
|
+
const s = JSON.stringify(value);
|
|
36
|
+
if (s && s.length > 5000)
|
|
37
|
+
return JSON.parse(s.slice(0, 5000) + '...(truncated)');
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=git-operation-log.js.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { WECHAT_BASE_URL, WECHAT_BOT_TYPE, WECHAT_QR_STATUS_TIMEOUT_MS, } from './notification-hub/types.js';
|
|
2
|
+
import { createAccount } from './robot-account.js';
|
|
3
|
+
const globalSessions = new Map();
|
|
4
|
+
export async function getGlobalWeChatQRCode() {
|
|
5
|
+
// cleanup old sessions
|
|
6
|
+
const now = Date.now();
|
|
7
|
+
for (const [key, s] of globalSessions) {
|
|
8
|
+
if (now - s.createdAt > 5 * 60_000)
|
|
9
|
+
globalSessions.delete(key);
|
|
10
|
+
}
|
|
11
|
+
const res = await fetch(`${WECHAT_BASE_URL}/ilink/bot/get_bot_qrcode?bot_type=${WECHAT_BOT_TYPE}`);
|
|
12
|
+
if (!res.ok)
|
|
13
|
+
throw new Error(`Failed to get WeChat QR code: ${res.status}`);
|
|
14
|
+
const qr = await res.json();
|
|
15
|
+
const sessionId = `global-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
16
|
+
globalSessions.set(sessionId, {
|
|
17
|
+
qrcode: qr.qrcode,
|
|
18
|
+
qrcodeImgContent: qr.qrcode_img_content,
|
|
19
|
+
createdAt: now,
|
|
20
|
+
});
|
|
21
|
+
return { status: 'wait', qrcodeImgContent: qr.qrcode_img_content, sessionId };
|
|
22
|
+
}
|
|
23
|
+
export async function pollGlobalWeChatQRCode(sessionId) {
|
|
24
|
+
if (!sessionId)
|
|
25
|
+
throw new Error('sessionId is required');
|
|
26
|
+
const session = globalSessions.get(sessionId);
|
|
27
|
+
if (!session)
|
|
28
|
+
throw new Error('Session not found or expired');
|
|
29
|
+
const status = await fetchQRStatus(session.qrcode);
|
|
30
|
+
if (status.status === 'confirmed') {
|
|
31
|
+
if (!status.bot_token || !status.ilink_bot_id) {
|
|
32
|
+
throw new Error('WeChat login confirmed but token or bot id is missing');
|
|
33
|
+
}
|
|
34
|
+
globalSessions.delete(sessionId);
|
|
35
|
+
const account = createAccount({
|
|
36
|
+
name: `微信 ${status.ilink_bot_id}`,
|
|
37
|
+
type: 'wechat',
|
|
38
|
+
wechat: {
|
|
39
|
+
token: status.bot_token,
|
|
40
|
+
baseUrl: status.baseurl || WECHAT_BASE_URL,
|
|
41
|
+
accountId: status.ilink_bot_id,
|
|
42
|
+
userId: status.ilink_user_id,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
return { status: 'confirmed', account };
|
|
46
|
+
}
|
|
47
|
+
if (status.status === 'expired') {
|
|
48
|
+
globalSessions.delete(sessionId);
|
|
49
|
+
}
|
|
50
|
+
return { status: status.status, qrcodeImgContent: session.qrcodeImgContent, sessionId };
|
|
51
|
+
}
|
|
52
|
+
async function fetchQRStatus(qrcode) {
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
const timer = setTimeout(() => controller.abort(), WECHAT_QR_STATUS_TIMEOUT_MS);
|
|
55
|
+
try {
|
|
56
|
+
const res = await fetch(`${WECHAT_BASE_URL}/ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrcode)}`, {
|
|
57
|
+
headers: { 'iLink-App-ClientVersion': '1' },
|
|
58
|
+
signal: controller.signal,
|
|
59
|
+
});
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
if (!res.ok)
|
|
62
|
+
throw new Error(`Failed to poll QR status: ${res.status}`);
|
|
63
|
+
return await res.json();
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
if (err instanceof Error && err.name === 'AbortError')
|
|
68
|
+
return { status: 'wait' };
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=global-wechat-qr.js.map
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { exec } from 'node:child_process';
|
|
2
2
|
import { listHooks } from '../storage/hook-store.js';
|
|
3
|
+
function logDebug(message, details) {
|
|
4
|
+
if (details) {
|
|
5
|
+
console.debug(`[HookEngine] ${message}`, details);
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
console.debug(`[HookEngine] ${message}`);
|
|
9
|
+
}
|
|
3
10
|
function matchToolName(matcher, toolName) {
|
|
4
11
|
if (matcher === '*')
|
|
5
12
|
return true;
|
|
@@ -8,7 +15,12 @@ function matchToolName(matcher, toolName) {
|
|
|
8
15
|
const regex = new RegExp(matcher.slice(1, -1));
|
|
9
16
|
return regex.test(toolName);
|
|
10
17
|
}
|
|
11
|
-
catch {
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.warn(`[HookEngine] invalid matcher regex, falling back to exact match`, {
|
|
20
|
+
matcher,
|
|
21
|
+
toolName,
|
|
22
|
+
error: error.message,
|
|
23
|
+
});
|
|
12
24
|
return matcher === toolName;
|
|
13
25
|
}
|
|
14
26
|
}
|
|
@@ -16,7 +28,8 @@ function matchToolName(matcher, toolName) {
|
|
|
16
28
|
}
|
|
17
29
|
function executeCommand(command, env, timeout) {
|
|
18
30
|
return new Promise((resolve) => {
|
|
19
|
-
|
|
31
|
+
const cappedTimeout = Math.min(timeout, 30000);
|
|
32
|
+
exec(command, { env: { ...process.env, ...env }, timeout: cappedTimeout }, (error) => {
|
|
20
33
|
if (error)
|
|
21
34
|
console.warn(`[HookEngine] command error: ${error.message}`);
|
|
22
35
|
resolve();
|
|
@@ -26,43 +39,101 @@ function executeCommand(command, env, timeout) {
|
|
|
26
39
|
async function executeWebhook(url, body, timeout) {
|
|
27
40
|
try {
|
|
28
41
|
const controller = new AbortController();
|
|
29
|
-
const
|
|
30
|
-
|
|
42
|
+
const cappedTimeout = Math.min(timeout, 30000);
|
|
43
|
+
const timer = setTimeout(() => controller.abort(), cappedTimeout);
|
|
44
|
+
const response = await fetch(url, {
|
|
31
45
|
method: 'POST',
|
|
32
46
|
headers: { 'Content-Type': 'application/json' },
|
|
33
47
|
body: JSON.stringify(body),
|
|
34
48
|
signal: controller.signal,
|
|
35
49
|
});
|
|
36
50
|
clearTimeout(timer);
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
console.warn(`[HookEngine] webhook non-2xx response`, {
|
|
53
|
+
url,
|
|
54
|
+
status: response.status,
|
|
55
|
+
statusText: response.statusText,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
37
58
|
}
|
|
38
59
|
catch (error) {
|
|
39
60
|
console.warn(`[HookEngine] webhook error: ${error.message}`);
|
|
40
61
|
}
|
|
41
62
|
}
|
|
42
|
-
async function executeRule(rule, phase,
|
|
63
|
+
async function executeRule(rule, phase, matcherValue, workspaceId, context, hookName) {
|
|
43
64
|
const timeout = rule.timeout ?? 10000;
|
|
65
|
+
const startedAt = Date.now();
|
|
66
|
+
logDebug('rule start', {
|
|
67
|
+
workspaceId,
|
|
68
|
+
phase,
|
|
69
|
+
hookName,
|
|
70
|
+
matcherValue,
|
|
71
|
+
matcher: rule.matcher,
|
|
72
|
+
type: rule.type,
|
|
73
|
+
timeout,
|
|
74
|
+
});
|
|
44
75
|
if (rule.type === 'command' && rule.command) {
|
|
45
76
|
await executeCommand(rule.command, {
|
|
46
|
-
|
|
77
|
+
HOOK_NAME: hookName ?? '',
|
|
78
|
+
HOOK_RULE_MATCHER: rule.matcher,
|
|
79
|
+
HOOK_RULE_TYPE: rule.type,
|
|
80
|
+
HOOK_RULE_TIMEOUT: String(timeout),
|
|
81
|
+
HOOK_EVENT_NAME: context.eventName ?? phase,
|
|
82
|
+
HOOK_EVENT_PAYLOAD: JSON.stringify(context.eventPayload ?? {}),
|
|
83
|
+
HOOK_MATCHER_VALUE: matcherValue,
|
|
84
|
+
HOOK_TOOL_NAME: matcherValue,
|
|
85
|
+
HOOK_TOOL_USE_ID: context.toolUseId ?? '',
|
|
47
86
|
HOOK_TOOL_INPUT: JSON.stringify(context.toolInput ?? {}),
|
|
48
87
|
HOOK_TOOL_RESULT: JSON.stringify(context.toolResult ?? ''),
|
|
49
88
|
HOOK_WORKSPACE_ID: workspaceId,
|
|
50
89
|
HOOK_PHASE: phase,
|
|
90
|
+
HOOK_TRIGGER_CHECKED_RULES: String(context.checkedRules ?? ''),
|
|
91
|
+
HOOK_TRIGGER_MATCHED_RULES: String(context.matchedRules ?? ''),
|
|
92
|
+
HOOK_TRIGGERED_AT: new Date().toISOString(),
|
|
51
93
|
}, timeout);
|
|
52
94
|
}
|
|
53
95
|
else if (rule.type === 'webhook' && rule.url) {
|
|
54
96
|
await executeWebhook(rule.url, {
|
|
55
97
|
event: phase,
|
|
56
|
-
toolName,
|
|
98
|
+
toolName: matcherValue,
|
|
99
|
+
matcherValue,
|
|
57
100
|
toolInput: context.toolInput,
|
|
58
101
|
toolResult: context.toolResult ?? undefined,
|
|
102
|
+
payload: context.eventPayload,
|
|
59
103
|
timestamp: new Date().toISOString(),
|
|
60
104
|
workspaceId,
|
|
61
105
|
}, timeout);
|
|
62
106
|
}
|
|
63
107
|
else if (rule.type === 'script') {
|
|
64
|
-
console.warn(`[HookEngine] script type not implemented, skipping rule
|
|
108
|
+
console.warn(`[HookEngine] script type not implemented, skipping rule`, {
|
|
109
|
+
workspaceId,
|
|
110
|
+
phase,
|
|
111
|
+
hookName,
|
|
112
|
+
matcherValue,
|
|
113
|
+
matcher: rule.matcher,
|
|
114
|
+
});
|
|
65
115
|
}
|
|
116
|
+
else {
|
|
117
|
+
console.warn(`[HookEngine] invalid rule configuration, skipping rule`, {
|
|
118
|
+
workspaceId,
|
|
119
|
+
phase,
|
|
120
|
+
hookName,
|
|
121
|
+
matcherValue,
|
|
122
|
+
matcher: rule.matcher,
|
|
123
|
+
type: rule.type,
|
|
124
|
+
hasCommand: Boolean(rule.command),
|
|
125
|
+
hasUrl: Boolean(rule.url),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
logDebug('rule finished', {
|
|
129
|
+
workspaceId,
|
|
130
|
+
phase,
|
|
131
|
+
hookName,
|
|
132
|
+
matcherValue,
|
|
133
|
+
matcher: rule.matcher,
|
|
134
|
+
type: rule.type,
|
|
135
|
+
durationMs: Date.now() - startedAt,
|
|
136
|
+
});
|
|
66
137
|
}
|
|
67
138
|
export class HookEngine {
|
|
68
139
|
hooks = [];
|
|
@@ -72,6 +143,12 @@ export class HookEngine {
|
|
|
72
143
|
}
|
|
73
144
|
load() {
|
|
74
145
|
this.hooks = listHooks(this.workspaceId);
|
|
146
|
+
logDebug('loaded hooks', {
|
|
147
|
+
workspaceId: this.workspaceId,
|
|
148
|
+
totalHooks: this.hooks.length,
|
|
149
|
+
enabledHooks: this.hooks.filter(h => h.enabled).length,
|
|
150
|
+
hookNames: this.hooks.map(h => h.name),
|
|
151
|
+
});
|
|
75
152
|
}
|
|
76
153
|
reload() {
|
|
77
154
|
this.load();
|
|
@@ -79,40 +156,121 @@ export class HookEngine {
|
|
|
79
156
|
getHooks() {
|
|
80
157
|
return this.hooks;
|
|
81
158
|
}
|
|
82
|
-
async executeHooks(phase,
|
|
159
|
+
async executeHooks(phase, matcherValue, context) {
|
|
83
160
|
const enabledHooks = this.hooks.filter(h => h.enabled);
|
|
84
161
|
const promises = [];
|
|
162
|
+
const matchedRuleEntries = [];
|
|
163
|
+
let checkedRules = 0;
|
|
164
|
+
let matchedRules = 0;
|
|
165
|
+
logDebug('trigger received', {
|
|
166
|
+
workspaceId: this.workspaceId,
|
|
167
|
+
phase,
|
|
168
|
+
matcherValue,
|
|
169
|
+
totalHooks: this.hooks.length,
|
|
170
|
+
enabledHooks: enabledHooks.length,
|
|
171
|
+
hasToolInput: context.toolInput !== undefined,
|
|
172
|
+
hasToolResult: context.toolResult !== undefined,
|
|
173
|
+
});
|
|
85
174
|
for (const hook of enabledHooks) {
|
|
86
175
|
const rules = hook.hooks[phase];
|
|
87
176
|
if (!rules)
|
|
88
177
|
continue;
|
|
89
178
|
for (const rule of rules) {
|
|
90
|
-
|
|
91
|
-
|
|
179
|
+
checkedRules += 1;
|
|
180
|
+
if (matchToolName(rule.matcher, matcherValue)) {
|
|
181
|
+
matchedRules += 1;
|
|
182
|
+
logDebug('rule matched', {
|
|
183
|
+
workspaceId: this.workspaceId,
|
|
184
|
+
phase,
|
|
185
|
+
hookName: hook.name,
|
|
186
|
+
matcherValue,
|
|
187
|
+
matcher: rule.matcher,
|
|
188
|
+
type: rule.type,
|
|
189
|
+
});
|
|
190
|
+
matchedRuleEntries.push({ hookName: hook.name, rule });
|
|
92
191
|
}
|
|
93
192
|
}
|
|
94
193
|
}
|
|
95
|
-
|
|
194
|
+
for (const { hookName, rule } of matchedRuleEntries) {
|
|
195
|
+
promises.push(executeRule(rule, phase, matcherValue, this.workspaceId, {
|
|
196
|
+
...context,
|
|
197
|
+
checkedRules,
|
|
198
|
+
matchedRules,
|
|
199
|
+
}, hookName));
|
|
200
|
+
}
|
|
201
|
+
const results = await Promise.allSettled(promises);
|
|
202
|
+
const rejectedRules = results.filter(result => result.status === 'rejected').length;
|
|
203
|
+
logDebug('trigger finished', {
|
|
204
|
+
workspaceId: this.workspaceId,
|
|
205
|
+
phase,
|
|
206
|
+
matcherValue,
|
|
207
|
+
checkedRules,
|
|
208
|
+
matchedRules,
|
|
209
|
+
rejectedRules,
|
|
210
|
+
});
|
|
96
211
|
}
|
|
97
212
|
}
|
|
98
213
|
export function wrapOnEventWithHooks(onEvent, workspaceId, hooksEnabled) {
|
|
99
|
-
if (!hooksEnabled)
|
|
214
|
+
if (!hooksEnabled) {
|
|
215
|
+
logDebug('hooks disabled for workspace', { workspaceId });
|
|
100
216
|
return onEvent;
|
|
217
|
+
}
|
|
101
218
|
const engine = new HookEngine(workspaceId);
|
|
102
219
|
engine.load();
|
|
103
220
|
const toolNameById = new Map();
|
|
221
|
+
let lastToolUse = null;
|
|
104
222
|
return (event) => {
|
|
105
|
-
|
|
223
|
+
if (event.type !== 'hook_event')
|
|
224
|
+
onEvent(event);
|
|
106
225
|
if (event.type === 'tool_use') {
|
|
107
226
|
toolNameById.set(event.id, event.name);
|
|
108
|
-
|
|
227
|
+
lastToolUse = { id: event.id, name: event.name };
|
|
228
|
+
logDebug('tool_use event mapped', {
|
|
229
|
+
workspaceId,
|
|
230
|
+
toolUseId: event.id,
|
|
231
|
+
toolName: event.name,
|
|
232
|
+
});
|
|
233
|
+
engine.executeHooks('PreToolUse', event.name, { toolInput: event.input, toolUseId: event.id });
|
|
109
234
|
}
|
|
110
235
|
if (event.type === 'tool_result') {
|
|
111
|
-
const
|
|
236
|
+
const resolvedToolUseId = event.toolUseId || lastToolUse?.id;
|
|
237
|
+
const toolName = resolvedToolUseId ? (toolNameById.get(resolvedToolUseId) ?? '') : '';
|
|
112
238
|
if (toolName) {
|
|
113
|
-
|
|
239
|
+
logDebug('tool_result event resolved', {
|
|
240
|
+
workspaceId,
|
|
241
|
+
toolUseId: resolvedToolUseId,
|
|
242
|
+
originalToolUseId: event.toolUseId,
|
|
243
|
+
toolName,
|
|
244
|
+
});
|
|
245
|
+
engine.executeHooks('PostToolUse', toolName, { toolResult: event.result, toolUseId: resolvedToolUseId });
|
|
246
|
+
if (isToolFailureResult(event.result)) {
|
|
247
|
+
engine.executeHooks('PostToolUseFailure', toolName, { toolResult: event.result, toolUseId: resolvedToolUseId });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
logDebug('tool_result event skipped without matching tool_use', {
|
|
252
|
+
workspaceId,
|
|
253
|
+
toolUseId: event.toolUseId,
|
|
254
|
+
});
|
|
114
255
|
}
|
|
115
256
|
}
|
|
257
|
+
if (event.type === 'hook_event') {
|
|
258
|
+
logDebug('hook_event received', {
|
|
259
|
+
workspaceId,
|
|
260
|
+
event: event.event,
|
|
261
|
+
matcher: event.matcher ?? '*',
|
|
262
|
+
});
|
|
263
|
+
engine.executeHooks(event.event, event.matcher ?? '*', {
|
|
264
|
+
eventName: event.event,
|
|
265
|
+
eventPayload: event.payload,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
116
268
|
};
|
|
117
269
|
}
|
|
270
|
+
function isToolFailureResult(result) {
|
|
271
|
+
if (!result || typeof result !== 'object')
|
|
272
|
+
return false;
|
|
273
|
+
const record = result;
|
|
274
|
+
return record.error !== undefined || record.is_error === true || record.success === false;
|
|
275
|
+
}
|
|
118
276
|
//# sourceMappingURL=hook-engine.js.map
|
|
@@ -28,7 +28,8 @@ export async function runBotAgent(workspaceId, preset, message) {
|
|
|
28
28
|
const workingDir = agentService.resolveWorkingDir(workspaceId, preset);
|
|
29
29
|
const workspace = workspaceService.getById(workspaceId);
|
|
30
30
|
try {
|
|
31
|
-
const
|
|
31
|
+
const userPrompt = buildBotPrompt(message);
|
|
32
|
+
const result = await runtime.execute(prependPersistentAgentContext(userPrompt, {
|
|
32
33
|
workspaceId,
|
|
33
34
|
workingDir,
|
|
34
35
|
boundDirs: workspace?.boundDirs,
|
|
@@ -36,6 +37,7 @@ export async function runBotAgent(workspaceId, preset, message) {
|
|
|
36
37
|
excludeNativeClaudeMd: preset.runtimeKind === 'claude-code',
|
|
37
38
|
}), workingDir, {
|
|
38
39
|
maxTurns: 20,
|
|
40
|
+
userPrompt,
|
|
39
41
|
mcpServers: agentService.getMcpServers(preset.mcps),
|
|
40
42
|
skills: agentService.getAvailableSkillNames(agentService.getAgentConfigDir(workspaceId, preset), preset.skills),
|
|
41
43
|
configDir: agentService.getAgentConfigDir(workspaceId, preset),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as workspaceService from '../workspace.js';
|
|
2
|
+
import { resolveCredentials } from '../robot-account.js';
|
|
2
3
|
import { adapters } from './types.js';
|
|
3
4
|
import { persistServiceRunning } from './helpers.js';
|
|
4
5
|
import { WeChatNotificationAdapter } from './wechat-adapter.js';
|
|
@@ -9,15 +10,24 @@ export async function startWorkspaceNotificationService(workspaceId) {
|
|
|
9
10
|
if (!workspace || !settings?.enabled)
|
|
10
11
|
return { started: false };
|
|
11
12
|
await stopWorkspaceNotificationService(workspaceId);
|
|
13
|
+
const credentials = resolveCredentials(settings);
|
|
12
14
|
if (settings.provider === 'lark') {
|
|
13
|
-
|
|
15
|
+
if (!credentials || credentials.type !== 'lark') {
|
|
16
|
+
throw new Error('Lark credentials not found. Link a Robot Account or configure appId/appSecret.');
|
|
17
|
+
}
|
|
18
|
+
const mergedSettings = { ...settings, lark: { ...settings.lark, appId: credentials.appId, appSecret: credentials.appSecret } };
|
|
19
|
+
const adapter = new LarkNotificationAdapter(workspace, mergedSettings);
|
|
14
20
|
await adapter.start();
|
|
15
21
|
adapters.set(workspaceId, adapter);
|
|
16
22
|
persistServiceRunning(workspaceId, true);
|
|
17
23
|
return { started: true, provider: 'lark' };
|
|
18
24
|
}
|
|
19
25
|
if (settings.provider === 'wechat') {
|
|
20
|
-
|
|
26
|
+
if (!credentials || credentials.type !== 'wechat') {
|
|
27
|
+
throw new Error('WeChat credentials not found. Link a Robot Account or scan QR code first.');
|
|
28
|
+
}
|
|
29
|
+
const mergedSettings = { ...settings, wechat: { ...settings.wechat, token: credentials.token, baseUrl: credentials.baseUrl, accountId: credentials.accountId, userId: credentials.userId } };
|
|
30
|
+
const adapter = new WeChatNotificationAdapter(workspace, mergedSettings);
|
|
21
31
|
await adapter.start();
|
|
22
32
|
adapters.set(workspaceId, adapter);
|
|
23
33
|
persistServiceRunning(workspaceId, true);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { v4 as uuid } from 'uuid';
|
|
2
|
+
import { listRobotAccounts, getRobotAccount, createRobotAccount, updateRobotAccount, deleteRobotAccount } from '../storage/robot-account-store.js';
|
|
3
|
+
export { listRobotAccounts, getRobotAccount, deleteRobotAccount };
|
|
4
|
+
export function createAccount(input) {
|
|
5
|
+
const now = new Date().toISOString();
|
|
6
|
+
const account = { id: uuid(), name: input.name, type: input.type, lark: input.lark, wechat: input.wechat, createdAt: now, updatedAt: now };
|
|
7
|
+
createRobotAccount(account);
|
|
8
|
+
return account;
|
|
9
|
+
}
|
|
10
|
+
export function updateAccount(id, patch) {
|
|
11
|
+
return updateRobotAccount(id, patch);
|
|
12
|
+
}
|
|
13
|
+
export function resolveCredentials(settings) {
|
|
14
|
+
if (settings.robotAccountId) {
|
|
15
|
+
const account = getRobotAccount(settings.robotAccountId);
|
|
16
|
+
if (account) {
|
|
17
|
+
if (account.type === 'lark' && account.lark) {
|
|
18
|
+
return { type: 'lark', appId: account.lark.appId, appSecret: account.lark.appSecret };
|
|
19
|
+
}
|
|
20
|
+
if (account.type === 'wechat' && account.wechat) {
|
|
21
|
+
return { type: 'wechat', token: account.wechat.token, baseUrl: account.wechat.baseUrl || 'https://qyapi.weixin.qq.com', accountId: account.wechat.accountId, userId: account.wechat.userId };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// fallback to inline credentials
|
|
26
|
+
if (settings.provider === 'lark' && settings.lark?.appId && settings.lark?.appSecret) {
|
|
27
|
+
return { type: 'lark', appId: settings.lark.appId, appSecret: settings.lark.appSecret };
|
|
28
|
+
}
|
|
29
|
+
if (settings.provider === 'wechat' && settings.wechat?.token && settings.wechat?.accountId) {
|
|
30
|
+
return { type: 'wechat', token: settings.wechat.token, baseUrl: settings.wechat.baseUrl || 'https://qyapi.weixin.qq.com', accountId: settings.wechat.accountId, userId: settings.wechat.userId };
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=robot-account.js.map
|
|
@@ -59,6 +59,8 @@ export function update(id, data) {
|
|
|
59
59
|
allowed.autoProcessIssues = data.autoProcessIssues;
|
|
60
60
|
if (Object.hasOwn(data, 'notificationSettings'))
|
|
61
61
|
allowed.notificationSettings = data.notificationSettings;
|
|
62
|
+
if (Object.hasOwn(data, 'hooksEnabled'))
|
|
63
|
+
allowed.hooksEnabled = data.hooksEnabled;
|
|
62
64
|
Object.assign(ws, allowed, { updatedAt: new Date().toISOString() });
|
|
63
65
|
updateWorkspace(ws);
|
|
64
66
|
return ws;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readJsonFile, writeJsonFile, getDataDir } from './json-store.js';
|
|
3
|
+
const FILE = () => join(getDataDir(), 'robot-accounts.json');
|
|
4
|
+
function read() {
|
|
5
|
+
return readJsonFile(FILE()) ?? { accounts: [] };
|
|
6
|
+
}
|
|
7
|
+
function write(data) {
|
|
8
|
+
writeJsonFile(FILE(), data);
|
|
9
|
+
}
|
|
10
|
+
export function listRobotAccounts() {
|
|
11
|
+
return read().accounts;
|
|
12
|
+
}
|
|
13
|
+
export function getRobotAccount(id) {
|
|
14
|
+
return read().accounts.find((a) => a.id === id);
|
|
15
|
+
}
|
|
16
|
+
export function createRobotAccount(account) {
|
|
17
|
+
const data = read();
|
|
18
|
+
data.accounts.push(account);
|
|
19
|
+
write(data);
|
|
20
|
+
}
|
|
21
|
+
export function updateRobotAccount(id, patch) {
|
|
22
|
+
const data = read();
|
|
23
|
+
const idx = data.accounts.findIndex((a) => a.id === id);
|
|
24
|
+
if (idx === -1)
|
|
25
|
+
return null;
|
|
26
|
+
data.accounts[idx] = { ...data.accounts[idx], ...patch, id, updatedAt: new Date().toISOString() };
|
|
27
|
+
write(data);
|
|
28
|
+
return data.accounts[idx];
|
|
29
|
+
}
|
|
30
|
+
export function deleteRobotAccount(id) {
|
|
31
|
+
const data = read();
|
|
32
|
+
const before = data.accounts.length;
|
|
33
|
+
data.accounts = data.accounts.filter((a) => a.id !== id);
|
|
34
|
+
if (data.accounts.length === before)
|
|
35
|
+
return false;
|
|
36
|
+
write(data);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=robot-account-store.js.map
|