@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.
- package/.forge/mcp.json +8 -0
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-316c6574/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-44a94121/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/07-projects.md +1 -1
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/01-settings.md +5 -5
- package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/07-projects.md +1 -1
- package/CLAUDE.md +2 -2
- package/RELEASE_NOTES.md +101 -5
- package/app/api/auth/check/route.ts +18 -0
- package/app/api/browser-bridge/route.ts +70 -0
- package/app/api/chat/sessions/[id]/events/route.ts +17 -0
- package/app/api/chat/sessions/[id]/fork/route.ts +15 -0
- package/app/api/chat/sessions/[id]/messages/route.ts +21 -0
- package/app/api/chat/sessions/[id]/route.ts +23 -0
- package/app/api/chat/sessions/route.ts +12 -0
- package/app/api/chat/temper-ping/route.ts +18 -0
- package/app/api/chat-proxy/[...path]/route.ts +83 -0
- package/app/api/connector-tool/route.ts +38 -0
- package/app/api/connectors/[id]/settings/route.ts +112 -0
- package/app/api/connectors/route.ts +108 -0
- package/app/api/health/tools/route.ts +14 -0
- package/app/api/issue-scanner-gitlab/route.ts +95 -0
- package/app/api/jobs/[id]/reset_dedup/route.ts +15 -0
- package/app/api/jobs/[id]/route.ts +31 -0
- package/app/api/jobs/[id]/run/route.ts +44 -0
- package/app/api/jobs/[id]/runs/[runId]/route.ts +15 -0
- package/app/api/jobs/[id]/runs/route.ts +15 -0
- package/app/api/jobs/preview/route.ts +193 -0
- package/app/api/jobs/route.ts +36 -0
- package/app/api/notify/test/route.ts +39 -7
- package/app/api/pipelines/[id]/route.ts +10 -1
- package/app/api/pipelines/route.ts +16 -2
- package/app/api/plugins/route.ts +40 -8
- package/app/api/project-sessions/route.ts +50 -10
- package/app/api/settings/route.ts +13 -0
- package/app/chat/page.tsx +531 -0
- package/bin/forge-server.mjs +3 -1
- package/cli/chat.ts +283 -0
- package/cli/jobs.ts +176 -0
- package/cli/mw.ts +28 -1
- package/cli/worktree.ts +245 -0
- package/components/ConnectorsPanel.tsx +275 -0
- package/components/Dashboard.tsx +90 -37
- package/components/JobsView.tsx +361 -0
- package/components/LogViewer.tsx +12 -2
- package/components/PipelineView.tsx +275 -56
- package/components/PluginsPanel.tsx +3 -1
- package/components/SettingsModal.tsx +229 -40
- package/components/SkillsPanel.tsx +12 -4
- package/components/TerminalLauncher.tsx +3 -1
- package/components/WebTerminal.tsx +32 -9
- package/components/WorkspaceView.tsx +18 -10
- package/docs/Connector-DeclarativeExtract-Handoff.md +471 -0
- package/docs/Connector-DeclarativeExtract-Spec.md +364 -0
- package/docs/Implementation-Plan-Browser-Agent.md +487 -0
- package/docs/Jobs-Design.md +240 -0
- package/docs/LOCAL-DEPLOY.md +3 -3
- package/docs/RFC-Browser-Connectors.md +509 -0
- package/lib/agents/index.ts +44 -6
- package/lib/agents/types.ts +1 -1
- package/lib/browser-bridge-standalone.ts +317 -0
- package/lib/builtin-plugins/github-api.yaml +93 -0
- package/lib/builtin-plugins/gitlab.yaml +860 -0
- package/lib/builtin-plugins/mantis.probe.js +176 -0
- package/lib/builtin-plugins/mantis.yaml +964 -0
- package/lib/builtin-plugins/pmdb.yaml +178 -0
- package/lib/builtin-plugins/teams.yaml +913 -0
- package/lib/chat/__test__/smoke.ts +30 -0
- package/lib/chat/agent-loop.ts +523 -0
- package/lib/chat/bridge-client.ts +59 -0
- package/lib/chat/llm/anthropic.ts +99 -0
- package/lib/chat/llm/index.ts +20 -0
- package/lib/chat/llm/openai.ts +215 -0
- package/lib/chat/llm/types.ts +42 -0
- package/lib/chat/local-memory.ts +300 -0
- package/lib/chat/memory-store.ts +87 -0
- package/lib/chat/memory-tools.ts +157 -0
- package/lib/chat/protocols/http.ts +118 -0
- package/lib/chat/protocols/shell.ts +101 -0
- package/lib/chat/proxy.ts +51 -0
- package/lib/chat/session-store.ts +272 -0
- package/lib/chat/telegram-bridge.ts +276 -0
- package/lib/chat/temper.ts +281 -0
- package/lib/chat/tool-dispatcher.ts +190 -0
- package/lib/chat/types.ts +50 -0
- package/lib/chat-standalone.ts +286 -0
- package/lib/crypto.ts +1 -1
- package/lib/health.ts +131 -0
- package/lib/help-docs/00-overview.md +2 -1
- package/lib/help-docs/01-settings.md +46 -25
- package/lib/help-docs/07-projects.md +1 -1
- package/lib/help-docs/10-troubleshooting.md +10 -2
- package/lib/help-docs/16-gitlab-autofix.md +114 -0
- package/lib/help-docs/17-connectors.md +322 -0
- package/lib/help-docs/18-chrome-mcp.md +134 -0
- package/lib/help-docs/19-jobs.md +140 -0
- package/lib/help-docs/20-mantis-bug-fix.md +115 -0
- package/lib/help-docs/CLAUDE.md +10 -0
- package/lib/init.ts +137 -50
- package/lib/iso-time.ts +30 -0
- package/lib/issue-scanner-gitlab.ts +281 -0
- package/lib/jobs/dispatcher.ts +217 -0
- package/lib/jobs/scheduler.ts +334 -0
- package/lib/jobs/store.ts +319 -0
- package/lib/jobs/types.ts +117 -0
- package/lib/pipeline-scheduler.ts +1 -6
- package/lib/pipeline.ts +790 -10
- package/lib/plugins/registry.ts +133 -8
- package/lib/plugins/templates.ts +83 -0
- package/lib/plugins/types.ts +140 -1
- package/lib/session-watcher.ts +36 -10
- package/lib/settings.ts +65 -33
- package/lib/skills.ts +3 -1
- package/lib/task-manager.ts +50 -22
- package/lib/telegram-bot.ts +71 -0
- package/lib/terminal-standalone.ts +58 -36
- package/lib/workspace/orchestrator.ts +1 -0
- package/middleware.ts +10 -0
- package/package.json +3 -2
- package/scripts/bench/README.md +1 -1
- package/scripts/bench/tasks/01-text-utils/validator.sh +1 -1
- package/scripts/bench/tasks/02-pagination/setup.sh +1 -1
- package/scripts/bench/tasks/02-pagination/validator.sh +1 -1
- package/scripts/bench/tasks/03-bug-fix/setup.sh +1 -1
- package/scripts/bench/tasks/03-bug-fix/validator.sh +1 -1
- package/src/core/db/database.ts +21 -12
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# Connector — Declarative Extraction Spec
|
|
2
|
+
|
|
3
|
+
> **Goal**: Adding a new browser-side connector should be a Forge-only change.
|
|
4
|
+
> No `pnpm build && release` of the browser extension required.
|
|
5
|
+
>
|
|
6
|
+
> **Today**: Forge ships the manifest (schema), Extension ships a hand-written
|
|
7
|
+
> handler (`mantis.ts`, eventually `gitlab.ts`, …). Adding a new connector
|
|
8
|
+
> means coordinated changes in two repos.
|
|
9
|
+
>
|
|
10
|
+
> **Target**: Manifest carries the extraction script. Extension is a generic
|
|
11
|
+
> runner that knows nothing about specific connectors.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Architecture before / after
|
|
16
|
+
|
|
17
|
+
### Today
|
|
18
|
+
```
|
|
19
|
+
Forge Extension
|
|
20
|
+
───── ─────────
|
|
21
|
+
mantis.yaml ──────────────▶ list of tool schemas
|
|
22
|
+
(schema only) │
|
|
23
|
+
│ LLM calls mantis.list_my_bugs
|
|
24
|
+
▼
|
|
25
|
+
mantis.ts (hand-written, ~300 LOC)
|
|
26
|
+
├─ findMantisTab() ◀── Mantis-specific
|
|
27
|
+
├─ ensureOnListPage() ◀── Mantis URLs hardcoded
|
|
28
|
+
├─ extractBugList() ◀── #bug_list selector hardcoded
|
|
29
|
+
└─ Forge column quirks ◀── per-instance hardcoded
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
chrome.scripting.executeScript → result
|
|
33
|
+
```
|
|
34
|
+
**Adding GitLab**: write `gitlab.ts` in extension, ship new extension version.
|
|
35
|
+
|
|
36
|
+
### Target
|
|
37
|
+
```
|
|
38
|
+
Forge Extension
|
|
39
|
+
───── ─────────
|
|
40
|
+
mantis.yaml src/lib/connectors/runner.ts
|
|
41
|
+
tools: (≤ 100 LOC, generic)
|
|
42
|
+
list_my_bugs:
|
|
43
|
+
page: { url, on_target, ... } │ LLM calls mantis.list_my_bugs
|
|
44
|
+
script: | ──────────────────────▶│
|
|
45
|
+
const list = document.qs(...) │
|
|
46
|
+
return { bugs, ... } ▼
|
|
47
|
+
runConnectorTool(plugin, tool, args)
|
|
48
|
+
├─ acquireTab(host_match, url)
|
|
49
|
+
├─ waitForLoad
|
|
50
|
+
├─ detectLoginRedirect
|
|
51
|
+
└─ executeScript(script, args) → result
|
|
52
|
+
```
|
|
53
|
+
**Adding GitLab**: drop `gitlab.yaml` in Forge with `page` + `script` per tool. Done.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 2. Schema change to plugin YAML
|
|
58
|
+
|
|
59
|
+
A connector plugin's `tools` map gets two new keys per tool:
|
|
60
|
+
`page` (where to extract from) and `script` (the in-page function body).
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
id: mantis
|
|
64
|
+
name: MantisBT
|
|
65
|
+
icon: "🐞"
|
|
66
|
+
version: "0.2.0" # bump on schema change
|
|
67
|
+
category: connector
|
|
68
|
+
mode: browser-side
|
|
69
|
+
|
|
70
|
+
settings:
|
|
71
|
+
base_url:
|
|
72
|
+
type: string
|
|
73
|
+
label: Mantis base URL
|
|
74
|
+
required: true
|
|
75
|
+
|
|
76
|
+
# plugin-level: how to find / open an authenticated tab on this host
|
|
77
|
+
host_match: "{base_url}/*" # Chrome match pattern, used to find existing tabs
|
|
78
|
+
login_redirect: "/login_page.php" # substring; if URL contains this after nav, abort
|
|
79
|
+
|
|
80
|
+
tools:
|
|
81
|
+
list_my_bugs:
|
|
82
|
+
description: List bugs assigned to me.
|
|
83
|
+
parameters:
|
|
84
|
+
status:
|
|
85
|
+
type: select
|
|
86
|
+
options: ["open", "closed", "all"]
|
|
87
|
+
default: "open"
|
|
88
|
+
limit:
|
|
89
|
+
type: number
|
|
90
|
+
default: 50
|
|
91
|
+
|
|
92
|
+
page:
|
|
93
|
+
url: "{base_url}/view_all_bug_page.php"
|
|
94
|
+
on_target: "/view_all_bug_page.php" # substring match; if current URL has this, don't navigate
|
|
95
|
+
|
|
96
|
+
# Function body. Receives `args` (the LLM's parsed parameters).
|
|
97
|
+
# Returns a JSON-serializable value. Top-level Web APIs only.
|
|
98
|
+
# NO closures over Forge / extension globals — this runs in the user's tab.
|
|
99
|
+
script: |
|
|
100
|
+
const { status, limit } = args;
|
|
101
|
+
const list = document.querySelector('#bug_list');
|
|
102
|
+
if (!list) return { bugs: [], total: 0, _error: '#bug_list not found' };
|
|
103
|
+
// ... ports the body of mantis.ts/extractBugList() ...
|
|
104
|
+
return { bugs, total, _page: location.href };
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Field reference
|
|
108
|
+
|
|
109
|
+
| Field | Level | Required | Notes |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `host_match` | plugin | yes (browser-side) | Used by extension to find/open tabs. Chrome match pattern with `{settings.*}` substitution. |
|
|
112
|
+
| `login_redirect` | plugin | optional | Substring. If the tab URL contains this after navigation, treat as "user not logged in". |
|
|
113
|
+
| `page.url` | tool | yes | Template URL to navigate to. `{base_url}` etc. expanded from settings. |
|
|
114
|
+
| `page.on_target` | tool | optional | If current tab URL contains this substring, skip the navigation step (current page is fine). |
|
|
115
|
+
| `script` | tool | yes | Function body. Implicit param: `args`. Must return a JSON-serializable value. |
|
|
116
|
+
|
|
117
|
+
### Multi-step tools (defer)
|
|
118
|
+
|
|
119
|
+
`add_comment`-style tools that POST need form interaction (fill input + click submit + wait for redirect). For v1 we only spec read-only extraction. Destructive tools stay marked `destructive: true` but **`script` is allowed to call `fetch()` from within the tab context** (same-origin, cookies auto-attached) for cases where the rendered HTML form is too brittle. That's a per-connector decision the script author makes.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 3. HTTP API change — `GET /api/connectors`
|
|
124
|
+
|
|
125
|
+
Today's response (per `app/api/connectors/route.ts`):
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"connectors": [
|
|
129
|
+
{
|
|
130
|
+
"plugin_id": "mantis",
|
|
131
|
+
"name": "MantisBT",
|
|
132
|
+
"mode": "browser-side",
|
|
133
|
+
"installed": true,
|
|
134
|
+
"entries": [
|
|
135
|
+
{
|
|
136
|
+
"id": "mantis",
|
|
137
|
+
"host_permissions": ["..."],
|
|
138
|
+
"tools": {
|
|
139
|
+
"list_my_bugs": {
|
|
140
|
+
"description": "...",
|
|
141
|
+
"parameters": {...},
|
|
142
|
+
"returns": "..."
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"settings": {...}
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
After this change, **`tools[name]` must additionally include `page` and `script`** (and the plugin level needs `host_match` + `login_redirect`):
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"connectors": [
|
|
158
|
+
{
|
|
159
|
+
"plugin_id": "mantis",
|
|
160
|
+
"name": "MantisBT",
|
|
161
|
+
"mode": "browser-side",
|
|
162
|
+
"installed": true,
|
|
163
|
+
"host_match": "https://mantis.acme.com/*",
|
|
164
|
+
"login_redirect": "/login_page.php",
|
|
165
|
+
"entries": [
|
|
166
|
+
{
|
|
167
|
+
"id": "mantis",
|
|
168
|
+
"tools": {
|
|
169
|
+
"list_my_bugs": {
|
|
170
|
+
"description": "...",
|
|
171
|
+
"parameters": {...},
|
|
172
|
+
"page": {
|
|
173
|
+
"url": "https://mantis.acme.com/view_all_bug_page.php",
|
|
174
|
+
"on_target": "/view_all_bug_page.php"
|
|
175
|
+
},
|
|
176
|
+
"script": "const { status, limit } = args; ... return { bugs, total };"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"settings": {...}
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Note `{base_url}` / `{settings.base_url}` is **expanded server-side** using the user's saved settings before the response goes out — extension shouldn't need to know template syntax. (If the user hasn't saved settings yet, omit the tool from the listing or return `null` for `page.url`.)
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 4. Template substitution rules
|
|
192
|
+
|
|
193
|
+
Anywhere `page.url`, `host_match`, `login_redirect` appear, expand `{<key>}` tokens:
|
|
194
|
+
|
|
195
|
+
- `{base_url}` → `settings.base_url` (with trailing slashes stripped)
|
|
196
|
+
- `{settings.<name>}` → `settings.<name>` (verbose form)
|
|
197
|
+
- Unknown tokens → leave as literal `{foo}` (don't error — gives author a way to ship a static URL with curly braces)
|
|
198
|
+
|
|
199
|
+
Do this in Forge before returning the manifest. Avoids re-implementing on the extension side.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 5. Script execution contract (what the extension will do)
|
|
204
|
+
|
|
205
|
+
The extension will:
|
|
206
|
+
1. Take `tool.script` as a string
|
|
207
|
+
2. Find or open a tab matching `host_match`, navigating to `page.url` unless `page.on_target` says current URL is fine
|
|
208
|
+
3. Detect `login_redirect` substring in tab URL → return `{ error: 'login required', loginRequired: true }`
|
|
209
|
+
4. Run via `chrome.scripting.executeScript`:
|
|
210
|
+
```js
|
|
211
|
+
// executes IN THE USER'S TAB (Mantis page context)
|
|
212
|
+
func: function(scriptText, args) {
|
|
213
|
+
try {
|
|
214
|
+
const fn = new Function('args', scriptText);
|
|
215
|
+
return { ok: true, value: fn(args) };
|
|
216
|
+
} catch (err) {
|
|
217
|
+
return { ok: false, error: err.message };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
5. Return `result.value` to the LLM as the tool result
|
|
222
|
+
|
|
223
|
+
**Script authors should assume**:
|
|
224
|
+
- `document`, `fetch`, `URL`, etc. are available
|
|
225
|
+
- Same-origin cookies are attached to `fetch()` calls
|
|
226
|
+
- No access to `chrome.*` APIs (running in page context)
|
|
227
|
+
- No closures over Forge/extension code — script is a self-contained function body
|
|
228
|
+
- Return value must be JSON-serializable (no DOM nodes, no functions)
|
|
229
|
+
- Errors should be caught and returned as `{ _error: '...' }` shape; thrown errors are also caught by the runner
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 6. Security considerations
|
|
234
|
+
|
|
235
|
+
This change moves trust: extension now executes arbitrary JS shipped by Forge. Constraints:
|
|
236
|
+
|
|
237
|
+
| Risk | Mitigation |
|
|
238
|
+
|---|---|
|
|
239
|
+
| Compromised Forge serves malicious script | Forge is already a trusted authenticated peer (`X-Forge-Token`). User explicitly chose to connect. Same trust level as `npm install`. |
|
|
240
|
+
| Script reads from / writes to sites it shouldn't | Run only in the tab matching `host_match`; never inject into arbitrary tabs. |
|
|
241
|
+
| `new Function` blocked by page CSP | Most internal corp tools have permissive CSPs. Strict-CSP sites (github.com, banks) will fail; document as a known limitation. For those, write a server-side connector instead (REST with token). |
|
|
242
|
+
| Script exfiltrates page content | Already inherent to the "scrape user's page" design — script could always do this. Extension UI should log connector tool calls so user can audit. |
|
|
243
|
+
|
|
244
|
+
**Recommendation for v1**: log every script run to extension console with `{plugin_id, tool, tab_url, script_length}`. Add a "Connector audit log" panel in Settings later.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 7. Concrete: full migration of `mantis.yaml`
|
|
249
|
+
|
|
250
|
+
The current `mantis.ts` in extension has these page-script blocks:
|
|
251
|
+
|
|
252
|
+
- `extractBugList(args)` — for `list_my_bugs`, runs on `/view_all_bug_page.php`
|
|
253
|
+
- `extractBugDetail(args)` — for `get_bug`, runs on `/bug_view_page.php?bug_id=N`
|
|
254
|
+
|
|
255
|
+
These two functions get **copy-pasted into the YAML's `script` fields** (minus type annotations — script is plain JS). Their function bodies are already self-contained, so the port is mechanical:
|
|
256
|
+
|
|
257
|
+
```yaml
|
|
258
|
+
id: mantis
|
|
259
|
+
name: MantisBT
|
|
260
|
+
version: "0.2.0"
|
|
261
|
+
category: connector
|
|
262
|
+
mode: browser-side
|
|
263
|
+
|
|
264
|
+
settings:
|
|
265
|
+
base_url: { type: string, required: true, label: "Mantis base URL" }
|
|
266
|
+
default_project: { type: string, label: "Default project" }
|
|
267
|
+
|
|
268
|
+
host_match: "{base_url}/*"
|
|
269
|
+
login_redirect: "/login_page.php"
|
|
270
|
+
|
|
271
|
+
tools:
|
|
272
|
+
list_my_bugs:
|
|
273
|
+
description: |
|
|
274
|
+
List bugs assigned to the current user.
|
|
275
|
+
parameters:
|
|
276
|
+
status:
|
|
277
|
+
type: select
|
|
278
|
+
options: ["open", "closed", "all"]
|
|
279
|
+
default: "open"
|
|
280
|
+
limit:
|
|
281
|
+
type: number
|
|
282
|
+
default: 50
|
|
283
|
+
page:
|
|
284
|
+
url: "{base_url}/view_all_bug_page.php"
|
|
285
|
+
on_target: "/view_all_bug_page.php"
|
|
286
|
+
script: |
|
|
287
|
+
// Body of current extractBugList in src/lib/connectors/mantis.ts
|
|
288
|
+
// (paste lines 69–148 of mantis.ts here, drop the function signature)
|
|
289
|
+
const list = document.querySelector('#bug_list');
|
|
290
|
+
if (!list) return { bugs: [], total: 0, _error: '#bug_list not found' };
|
|
291
|
+
// ... etc
|
|
292
|
+
return { bugs: filtered.slice(0, args.limit), total: filtered.length };
|
|
293
|
+
|
|
294
|
+
get_bug:
|
|
295
|
+
description: |
|
|
296
|
+
Get full details of a single bug.
|
|
297
|
+
parameters:
|
|
298
|
+
bug_id:
|
|
299
|
+
type: number
|
|
300
|
+
required: true
|
|
301
|
+
page:
|
|
302
|
+
# Note: bug_id is in args, so we use it in the URL via template
|
|
303
|
+
# extension expands `{args.bug_id}` at run-time (this is the one case
|
|
304
|
+
# where args, not settings, drives the URL — special-case in spec).
|
|
305
|
+
url: "{base_url}/bug_view_page.php?bug_id={args.bug_id}"
|
|
306
|
+
on_target: "/bug_view_page.php"
|
|
307
|
+
script: |
|
|
308
|
+
// Body of current extractBugDetail
|
|
309
|
+
...
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
> ⚠ **`{args.*}` substitution in URLs is the one extra template feature** —
|
|
313
|
+
> needed for tools that target a specific record by ID. Forge needs to do
|
|
314
|
+
> two passes: settings-time substitution at API response time, then args-time
|
|
315
|
+
> substitution inside the extension when actually executing the tool. (Or
|
|
316
|
+
> Forge can leave `{args.*}` literals unexpanded; the extension finishes
|
|
317
|
+
> them.) **Recommendation**: leave `{args.*}` for the extension to expand
|
|
318
|
+
> from the LLM's input; Forge only expands `{settings.*}` / `{base_url}`.
|
|
319
|
+
|
|
320
|
+
The `mantis.probe.js` file stays as a dev tool; it's not shipped to extension at runtime.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## 8. Migration order
|
|
325
|
+
|
|
326
|
+
To avoid breaking the extension during the change:
|
|
327
|
+
|
|
328
|
+
1. **Forge: add the schema fields, no behavior change**
|
|
329
|
+
- Extend manifest parser to accept `page` + `script` + `host_match` + `login_redirect`. Tools missing them are still valid (old shape).
|
|
330
|
+
- Update `/api/connectors` to emit the new fields when present, omit when absent.
|
|
331
|
+
- Mantis manifest gains `page` + `script` for both tools. Bump version `0.1.1` → `0.2.0`.
|
|
332
|
+
- Verify with curl: `/api/connectors/mantis` returns the new shape, fields populated.
|
|
333
|
+
|
|
334
|
+
2. **Extension: write the generic runner alongside the existing per-connector handler**
|
|
335
|
+
- New file `src/lib/connectors/runner.ts` implementing `runConnectorTool(connector, toolName, input)`.
|
|
336
|
+
- Update `build-tools.ts` so a tool is "executable" if its manifest has `script` (not "if `hasHandler(plugin_id)`").
|
|
337
|
+
- Routing: if tool has `script` → runner; else (fallback) → old `dispatchConnector(plugin_id, …)` for backward compat.
|
|
338
|
+
|
|
339
|
+
3. **Cut over**
|
|
340
|
+
- Once the runner works for `mantis.list_my_bugs` end-to-end, delete `src/lib/connectors/mantis.ts` and the `mantis` entry in `registry.ts`.
|
|
341
|
+
- Remove the fallback branch in routing.
|
|
342
|
+
|
|
343
|
+
4. **Add second connector to prove the loop**
|
|
344
|
+
- Pick the simplest GitLab page (e.g. "my issues") and write the YAML. Confirm extension picks it up with zero extension-side changes.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 9. Acceptance criteria
|
|
349
|
+
|
|
350
|
+
A connector is "shippable via Forge-only changes" iff:
|
|
351
|
+
|
|
352
|
+
- ✅ Adding a new `<name>.yaml` in `lib/builtin-plugins/` makes the tool visible in extension on next Connectors-tab refresh.
|
|
353
|
+
- ✅ Saving connector settings (`base_url`) makes the tool callable by the LLM.
|
|
354
|
+
- ✅ LLM calls succeed end-to-end without rebuilding/reloading the extension binary.
|
|
355
|
+
- ✅ Existing `mantis` connector continues to work throughout the migration (no flag day).
|
|
356
|
+
- ✅ Bumping a connector's `version` and changing only the YAML's `script` body propagates to all extension users on next refresh — no extension release needed.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## 10. Open questions for Forge side
|
|
361
|
+
|
|
362
|
+
- [ ] Where does the YAML's `script` body actually live in a multi-line YAML? Confirm Forge's YAML parser preserves it verbatim (it should — block scalar `|` is the right syntax).
|
|
363
|
+
- [ ] For users on `installed=false`, omit `page.url` / `script` from the response, or include them so the marketplace UI can preview? (Recommendation: include, but with `{settings.base_url}` left literal, so consumer can preview.)
|
|
364
|
+
- [ ] Probe scripts (`mantis.probe.js`) — keep as devtool only, or also serve via `/api/connectors/<id>/probe` so the extension can re-run probe on a "selectors broken" report? (Recommendation: defer — solve when selectors actually break.)
|