@aion0/forge 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/.forge/mcp.json +8 -0
  2. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/01-settings.md +5 -5
  3. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/07-projects.md +1 -1
  4. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/01-settings.md +5 -5
  5. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/07-projects.md +1 -1
  6. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/01-settings.md +5 -5
  7. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/07-projects.md +1 -1
  8. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/01-settings.md +5 -5
  9. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/07-projects.md +1 -1
  10. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/01-settings.md +5 -5
  11. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/07-projects.md +1 -1
  12. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +5 -5
  13. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +1 -1
  14. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/01-settings.md +5 -5
  15. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/07-projects.md +1 -1
  16. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/01-settings.md +5 -5
  17. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/07-projects.md +1 -1
  18. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/01-settings.md +5 -5
  19. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/07-projects.md +1 -1
  20. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/01-settings.md +5 -5
  21. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/07-projects.md +1 -1
  22. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/01-settings.md +5 -5
  23. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/07-projects.md +1 -1
  24. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/01-settings.md +5 -5
  25. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/07-projects.md +1 -1
  26. package/CLAUDE.md +2 -2
  27. package/RELEASE_NOTES.md +101 -5
  28. package/app/api/auth/check/route.ts +18 -0
  29. package/app/api/browser-bridge/route.ts +70 -0
  30. package/app/api/chat/sessions/[id]/events/route.ts +17 -0
  31. package/app/api/chat/sessions/[id]/fork/route.ts +15 -0
  32. package/app/api/chat/sessions/[id]/messages/route.ts +21 -0
  33. package/app/api/chat/sessions/[id]/route.ts +23 -0
  34. package/app/api/chat/sessions/route.ts +12 -0
  35. package/app/api/chat/temper-ping/route.ts +18 -0
  36. package/app/api/chat-proxy/[...path]/route.ts +83 -0
  37. package/app/api/connector-tool/route.ts +38 -0
  38. package/app/api/connectors/[id]/settings/route.ts +112 -0
  39. package/app/api/connectors/route.ts +108 -0
  40. package/app/api/health/tools/route.ts +14 -0
  41. package/app/api/issue-scanner-gitlab/route.ts +95 -0
  42. package/app/api/jobs/[id]/reset_dedup/route.ts +15 -0
  43. package/app/api/jobs/[id]/route.ts +31 -0
  44. package/app/api/jobs/[id]/run/route.ts +44 -0
  45. package/app/api/jobs/[id]/runs/[runId]/route.ts +15 -0
  46. package/app/api/jobs/[id]/runs/route.ts +15 -0
  47. package/app/api/jobs/preview/route.ts +193 -0
  48. package/app/api/jobs/route.ts +36 -0
  49. package/app/api/notify/test/route.ts +39 -7
  50. package/app/api/pipelines/[id]/route.ts +10 -1
  51. package/app/api/pipelines/route.ts +16 -2
  52. package/app/api/plugins/route.ts +40 -8
  53. package/app/api/project-sessions/route.ts +50 -10
  54. package/app/api/settings/route.ts +13 -0
  55. package/app/chat/page.tsx +531 -0
  56. package/bin/forge-server.mjs +3 -1
  57. package/cli/chat.ts +283 -0
  58. package/cli/jobs.ts +176 -0
  59. package/cli/mw.ts +28 -1
  60. package/cli/worktree.ts +245 -0
  61. package/components/ConnectorsPanel.tsx +275 -0
  62. package/components/Dashboard.tsx +90 -37
  63. package/components/JobsView.tsx +361 -0
  64. package/components/LogViewer.tsx +12 -2
  65. package/components/PipelineView.tsx +275 -56
  66. package/components/PluginsPanel.tsx +3 -1
  67. package/components/SettingsModal.tsx +229 -40
  68. package/components/SkillsPanel.tsx +12 -4
  69. package/components/TerminalLauncher.tsx +3 -1
  70. package/components/WebTerminal.tsx +32 -9
  71. package/components/WorkspaceView.tsx +18 -10
  72. package/docs/Connector-DeclarativeExtract-Handoff.md +471 -0
  73. package/docs/Connector-DeclarativeExtract-Spec.md +364 -0
  74. package/docs/Implementation-Plan-Browser-Agent.md +487 -0
  75. package/docs/Jobs-Design.md +240 -0
  76. package/docs/LOCAL-DEPLOY.md +3 -3
  77. package/docs/RFC-Browser-Connectors.md +509 -0
  78. package/lib/agents/index.ts +44 -6
  79. package/lib/agents/types.ts +1 -1
  80. package/lib/browser-bridge-standalone.ts +317 -0
  81. package/lib/builtin-plugins/github-api.yaml +93 -0
  82. package/lib/builtin-plugins/gitlab.yaml +860 -0
  83. package/lib/builtin-plugins/mantis.probe.js +176 -0
  84. package/lib/builtin-plugins/mantis.yaml +964 -0
  85. package/lib/builtin-plugins/pmdb.yaml +178 -0
  86. package/lib/builtin-plugins/teams.yaml +913 -0
  87. package/lib/chat/__test__/smoke.ts +30 -0
  88. package/lib/chat/agent-loop.ts +523 -0
  89. package/lib/chat/bridge-client.ts +59 -0
  90. package/lib/chat/llm/anthropic.ts +99 -0
  91. package/lib/chat/llm/index.ts +20 -0
  92. package/lib/chat/llm/openai.ts +215 -0
  93. package/lib/chat/llm/types.ts +42 -0
  94. package/lib/chat/local-memory.ts +300 -0
  95. package/lib/chat/memory-store.ts +87 -0
  96. package/lib/chat/memory-tools.ts +157 -0
  97. package/lib/chat/protocols/http.ts +118 -0
  98. package/lib/chat/protocols/shell.ts +101 -0
  99. package/lib/chat/proxy.ts +51 -0
  100. package/lib/chat/session-store.ts +272 -0
  101. package/lib/chat/telegram-bridge.ts +276 -0
  102. package/lib/chat/temper.ts +281 -0
  103. package/lib/chat/tool-dispatcher.ts +190 -0
  104. package/lib/chat/types.ts +50 -0
  105. package/lib/chat-standalone.ts +286 -0
  106. package/lib/crypto.ts +1 -1
  107. package/lib/health.ts +131 -0
  108. package/lib/help-docs/00-overview.md +2 -1
  109. package/lib/help-docs/01-settings.md +46 -25
  110. package/lib/help-docs/07-projects.md +1 -1
  111. package/lib/help-docs/10-troubleshooting.md +10 -2
  112. package/lib/help-docs/16-gitlab-autofix.md +114 -0
  113. package/lib/help-docs/17-connectors.md +322 -0
  114. package/lib/help-docs/18-chrome-mcp.md +134 -0
  115. package/lib/help-docs/19-jobs.md +140 -0
  116. package/lib/help-docs/20-mantis-bug-fix.md +115 -0
  117. package/lib/help-docs/CLAUDE.md +10 -0
  118. package/lib/init.ts +137 -50
  119. package/lib/iso-time.ts +30 -0
  120. package/lib/issue-scanner-gitlab.ts +281 -0
  121. package/lib/jobs/dispatcher.ts +217 -0
  122. package/lib/jobs/scheduler.ts +334 -0
  123. package/lib/jobs/store.ts +319 -0
  124. package/lib/jobs/types.ts +117 -0
  125. package/lib/pipeline-scheduler.ts +1 -6
  126. package/lib/pipeline.ts +790 -10
  127. package/lib/plugins/registry.ts +133 -8
  128. package/lib/plugins/templates.ts +83 -0
  129. package/lib/plugins/types.ts +140 -1
  130. package/lib/session-watcher.ts +36 -10
  131. package/lib/settings.ts +65 -33
  132. package/lib/skills.ts +3 -1
  133. package/lib/task-manager.ts +50 -22
  134. package/lib/telegram-bot.ts +71 -0
  135. package/lib/terminal-standalone.ts +58 -36
  136. package/lib/workspace/orchestrator.ts +1 -0
  137. package/middleware.ts +10 -0
  138. package/package.json +3 -2
  139. package/scripts/bench/README.md +1 -1
  140. package/scripts/bench/tasks/01-text-utils/validator.sh +1 -1
  141. package/scripts/bench/tasks/02-pagination/setup.sh +1 -1
  142. package/scripts/bench/tasks/02-pagination/validator.sh +1 -1
  143. package/scripts/bench/tasks/03-bug-fix/setup.sh +1 -1
  144. package/scripts/bench/tasks/03-bug-fix/validator.sh +1 -1
  145. package/src/core/db/database.ts +21 -12
@@ -0,0 +1,240 @@
1
+ # Jobs — scheduled connector polls that fan out to pipelines or chat
2
+
3
+ ## What a Job is
4
+
5
+ A Job is a new Forge primitive (alongside Task and Pipeline). It periodically
6
+ calls a **connector tool** and, for every new item in the result, **dispatches**
7
+ to either a Pipeline run or a Chat session.
8
+
9
+ Typical use cases:
10
+ - Every 30 min: `teams.list_messages_in_current_chat` → for each unread → POST
11
+ to a chat session that asks the assistant to summarize and reply.
12
+ - Every hour: `mantis.search_bugs query="version:25.4 status:open"` → for each
13
+ new bug → trigger the `bug-triage` pipeline bound to project `my-app` with
14
+ `bug_id`, `summary` as params.
15
+
16
+ Job vs the other two:
17
+
18
+ | Concept | Triggered by | Does what |
19
+ |---|---|---|
20
+ | Task (existing) | manual / Telegram / API | one CLI-agent invocation |
21
+ | Pipeline (existing) | manual / schedule / GitHub issue / **Job** | DAG of CLI-agent tasks |
22
+ | **Job** (new) | scheduler tick (every `schedule_interval_minutes`) | calls one connector tool, dedups, dispatches per item |
23
+
24
+ The CLI keeps `forge task` / `forge pipeline` unchanged. A new `forge jobs`
25
+ subcommand handles Job CRUD + manual trigger.
26
+
27
+ ## Storage (all sqlite)
28
+
29
+ The Job definition lives in sqlite — no yaml files — so the extension can
30
+ create / edit / delete jobs without writing to disk.
31
+
32
+ ```sql
33
+ CREATE TABLE IF NOT EXISTS jobs (
34
+ id TEXT PRIMARY KEY, -- short uuid
35
+ name TEXT NOT NULL,
36
+ enabled INTEGER NOT NULL DEFAULT 1,
37
+ schedule_interval_minutes INTEGER NOT NULL DEFAULT 30,
38
+
39
+ -- Source: which connector tool to call each tick
40
+ source_connector TEXT NOT NULL, -- plugin_id, e.g. 'mantis'
41
+ source_tool TEXT NOT NULL, -- tool name, e.g. 'search_bugs'
42
+ source_input TEXT NOT NULL DEFAULT '{}', -- JSON: passed verbatim as tool input
43
+
44
+ -- How to find items in the tool result + dedup key
45
+ items_path TEXT, -- dotted path; empty = whole result if array
46
+ dedup_field TEXT NOT NULL, -- dotted path on each item, e.g. 'id' or 'meta.uuid'
47
+
48
+ -- Dispatch (one of two modes)
49
+ dispatch_type TEXT NOT NULL, -- 'pipeline' | 'chat'
50
+ dispatch_params TEXT NOT NULL DEFAULT '{}', -- JSON; shape depends on type — see below
51
+
52
+ -- Bookkeeping
53
+ last_run_at TEXT,
54
+ next_run_at TEXT,
55
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
56
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
57
+ );
58
+
59
+ CREATE TABLE IF NOT EXISTS job_runs (
60
+ id TEXT PRIMARY KEY,
61
+ job_id TEXT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
62
+ started_at TEXT NOT NULL DEFAULT (datetime('now')),
63
+ finished_at TEXT,
64
+ status TEXT NOT NULL DEFAULT 'running', -- running | ok | error
65
+ items_seen INTEGER NOT NULL DEFAULT 0,
66
+ items_new INTEGER NOT NULL DEFAULT 0,
67
+ items_dispatched INTEGER NOT NULL DEFAULT 0,
68
+ error TEXT,
69
+ trigger TEXT NOT NULL DEFAULT 'schedule' -- schedule | manual
70
+ );
71
+
72
+ CREATE TABLE IF NOT EXISTS job_seen (
73
+ job_id TEXT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
74
+ dedup_key TEXT NOT NULL,
75
+ first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
76
+ PRIMARY KEY (job_id, dedup_key)
77
+ );
78
+
79
+ CREATE TABLE IF NOT EXISTS job_dispatches (
80
+ id TEXT PRIMARY KEY,
81
+ job_run_id TEXT NOT NULL REFERENCES job_runs(id) ON DELETE CASCADE,
82
+ item_key TEXT NOT NULL,
83
+ item_preview TEXT, -- short rendering for the UI
84
+ dispatch_type TEXT NOT NULL, -- mirrored from job
85
+ dispatch_target_id TEXT, -- pipeline_id OR chat session_id
86
+ status TEXT NOT NULL DEFAULT 'dispatched', -- dispatched | error
87
+ error TEXT,
88
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
89
+ );
90
+
91
+ CREATE INDEX IF NOT EXISTS idx_job_runs_job ON job_runs(job_id, started_at DESC);
92
+ CREATE INDEX IF NOT EXISTS idx_job_dispatches_run ON job_dispatches(job_run_id, created_at DESC);
93
+ ```
94
+
95
+ ## dispatch_params shape
96
+
97
+ ### `dispatch_type = 'pipeline'`
98
+
99
+ ```json
100
+ {
101
+ "workflow_name": "bug-triage",
102
+ "project_path": "/Users/me/code/my-app",
103
+ "project_name": "my-app",
104
+ "input_template": {
105
+ "bug_id": "{{item.id}}",
106
+ "summary": "{{item.summary}}",
107
+ "priority": "{{item.priority}}"
108
+ }
109
+ }
110
+ ```
111
+
112
+ Per item: render `input_template` (string substitution `{{item.<path>}}`), then
113
+ call existing `triggerPipeline(project_path, project_name, workflow_name,
114
+ rendered_input, dedup_key=item_key)`.
115
+
116
+ ### `dispatch_type = 'chat'`
117
+
118
+ ```json
119
+ {
120
+ "agent_profile": "my-litellm", // optional; falls back to settings.chatAgent
121
+ "session_title_template": "Bug {{item.id}}: {{item.summary}}",
122
+ "message_template": "Process Mantis bug {{item.id}} (priority {{item.priority}}).\nSummary: {{item.summary}}\n\nFind the relevant branch and prepare a triage note.",
123
+ "reuse_session": false // if true, all items append to one session; else fresh session per item
124
+ }
125
+ ```
126
+
127
+ Per item: POST `/api/sessions` (or reuse) on chat-standalone, then POST a
128
+ message rendered from `message_template`. The chat agent gets the item and
129
+ decides what tools to call (could be another connector call, code edit via
130
+ shell, etc.).
131
+
132
+ ## Template syntax
133
+
134
+ Values inside `{{ ... }}` use dotted paths into the item:
135
+ - `{{item.id}}` → item.id
136
+ - `{{item.assignee.name}}` → item.assignee.name
137
+ - `{{item}}` → JSON.stringify(item) (for dumping into a chat message)
138
+
139
+ Missing paths render to empty string. Non-string values render via
140
+ `String(...)` (numbers/booleans) or `JSON.stringify(...)` (objects).
141
+
142
+ Only `{{item.*}}` is supported in v1. `{{now}}` / `{{settings.*}}` are not
143
+ needed yet.
144
+
145
+ ## Scheduler
146
+
147
+ Runs inside the same Next.js worker as `pipeline-scheduler` (started in
148
+ `lib/init.ts`). Every 60s:
149
+
150
+ 1. Load all `enabled = 1` jobs where `next_run_at IS NULL OR next_run_at <= now`.
151
+ 2. For each: insert a `job_runs` row, dispatch the tick asynchronously, advance
152
+ `next_run_at = now + interval_minutes`, set `last_run_at = now`.
153
+ 3. Per-tick work happens in a background promise (HTTP / SSE / spawn don't block
154
+ the scheduler loop).
155
+
156
+ A tick is **idempotent** — if the previous tick is still running for the same
157
+ job (run row marked `status = 'running'` with no `finished_at`), the next tick
158
+ skips it and logs.
159
+
160
+ ## A tick in detail
161
+
162
+ 1. Resolve the connector + tool definition via `lib/plugins/registry` (same
163
+ path the chat dispatcher uses).
164
+ 2. Call `dispatchTool({id: '<job-run-id>', name: '<connector>.<tool>', input:
165
+ source_input})` from `lib/chat/tool-dispatcher`. This handles `http` /
166
+ `shell` / `browser` uniformly. (Browser-protocol jobs require the extension
167
+ bridge to be connected at tick time — if not, surface as run error.)
168
+ 3. Parse the tool's `content` string as JSON. Apply `items_path` to find the
169
+ array (e.g. `bugs` → `result.bugs`). If parsed value is already an array,
170
+ use it directly.
171
+ 4. For each item:
172
+ - Compute `key = pickPath(item, dedup_field)`. Stringify.
173
+ - `INSERT OR IGNORE INTO job_seen (job_id, dedup_key)` — if a new row was
174
+ written, it's a new item.
175
+ - For new items only: dispatch via the configured type, insert into
176
+ `job_dispatches` with the resulting target id.
177
+ 5. Update `job_runs` row with counts + finished_at + status.
178
+
179
+ ## API surface
180
+
181
+ ```
182
+ GET /api/jobs list (enabled+disabled)
183
+ POST /api/jobs create
184
+ GET /api/jobs/:id detail (with last 20 runs)
185
+ PATCH /api/jobs/:id partial update
186
+ DELETE /api/jobs/:id cascade delete
187
+ POST /api/jobs/:id/run manual fire-now (returns run id)
188
+ GET /api/jobs/:id/runs?limit=20 run history
189
+ GET /api/jobs/:id/runs/:run_id run detail (with dispatches)
190
+ POST /api/jobs/:id/reset_dedup wipe job_seen rows (force re-process all)
191
+ ```
192
+
193
+ All gated by the existing Forge middleware (X-Forge-Token).
194
+
195
+ ## CLI
196
+
197
+ ```
198
+ forge jobs list
199
+ forge jobs new <interactive prompts> create
200
+ forge jobs run <id> fire now
201
+ forge jobs runs <id> history
202
+ forge jobs rm <id>
203
+ forge jobs disable <id> / enable <id>
204
+ forge jobs reset <id> wipe dedup state
205
+ ```
206
+
207
+ ## Extension UI (Tasks → Jobs)
208
+
209
+ `TasksTab.tsx` renames to `JobsTab.tsx`. Tab label "Tasks" → "Jobs".
210
+
211
+ Sections:
212
+ 1. **Active jobs** — for each: name, schedule, last run time + items new/seen,
213
+ next run countdown, enable toggle, "Run now" button.
214
+ 2. **+ New job** — modal:
215
+ - name, interval
216
+ - source: connector dropdown (from `/api/connectors`), tool dropdown
217
+ (populated from selected connector's tools), input JSON
218
+ - items_path + dedup_field (with examples per connector)
219
+ - dispatch type radio: pipeline | chat
220
+ - pipeline: workflow dropdown (`/api/pipelines/workflows`), project
221
+ dropdown (`/api/projects`), input_template builder
222
+ - chat: agent profile dropdown (`/api/agents`), session_title template,
223
+ message_template (textarea), reuse_session toggle
224
+ 3. **Recent runs** (collapsed by default) — last 20 across all jobs.
225
+
226
+ Per architecture rule: extension is display + DOM connectors only. All
227
+ scheduling, dedup, dispatch happens server-side. The extension talks to Forge
228
+ HTTP only — no local state for jobs.
229
+
230
+ ## Out of scope (v1)
231
+
232
+ - Cron expressions (interval-only)
233
+ - Item filtering before dispatch (just write the connector tool to return
234
+ filtered)
235
+ - Conditional dispatch (always-fire on new item; for skip-logic, use the
236
+ pipeline / chat to decide internally)
237
+ - Webhook triggers (still poll-based)
238
+ - Backfill controls (initial tick auto-marks everything as seen without
239
+ dispatching, so a brand-new job doesn't fire on history — controlled by
240
+ `mark_existing_as_seen` flag at create time, default true)
@@ -90,14 +90,14 @@ caffeinate -d -i -s &
90
90
  ### 注册为系统服务(开机自启 + 崩溃自重启)
91
91
 
92
92
  ```xml
93
- <!-- ~/Library/LaunchAgents/com.zliu.my-workflow.plist -->
93
+ <!-- ~/Library/LaunchAgents/com.forge.my-workflow.plist -->
94
94
  <?xml version="1.0" encoding="UTF-8"?>
95
95
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
96
96
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
97
97
  <plist version="1.0">
98
98
  <dict>
99
99
  <key>Label</key>
100
- <string>com.zliu.my-workflow</string>
100
+ <string>com.forge.my-workflow</string>
101
101
  <key>ProgramArguments</key>
102
102
  <array>
103
103
  <string>/usr/local/bin/mw</string>
@@ -119,7 +119,7 @@ caffeinate -d -i -s &
119
119
 
120
120
  ```bash
121
121
  # 注册服务
122
- launchctl load ~/Library/LaunchAgents/com.zliu.my-workflow.plist
122
+ launchctl load ~/Library/LaunchAgents/com.forge.my-workflow.plist
123
123
 
124
124
  # 查看状态
125
125
  launchctl list | grep my-workflow