@aion0/forge 0.8.1 → 0.8.3
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/RELEASE_NOTES.md +6 -6
- package/app/api/connectors/[id]/settings/route.ts +31 -37
- package/app/api/connectors/[id]/test/route.ts +260 -0
- package/app/api/connectors/install-local/route.ts +211 -0
- package/app/api/connectors/marketplace/route.ts +79 -0
- package/app/api/connectors/route.ts +41 -46
- package/app/api/jobs/route.ts +4 -0
- package/app/api/skills/install-local/route.ts +282 -0
- package/components/ConnectorsPanel.tsx +526 -211
- package/components/SettingsModal.tsx +1 -0
- package/components/SkillsPanel.tsx +42 -1
- package/lib/agents/claude-adapter.ts +4 -0
- package/lib/agents/types.ts +6 -0
- package/lib/chat/agent-loop.ts +13 -22
- package/lib/chat/protocols/http.ts +1 -1
- package/lib/chat/protocols/shell.ts +1 -1
- package/lib/chat/tool-dispatcher.ts +20 -20
- package/lib/connectors/migration.ts +110 -0
- package/lib/connectors/registry.ts +328 -0
- package/lib/connectors/sync.ts +305 -0
- package/lib/connectors/types.ts +253 -0
- package/lib/help-docs/00-overview.md +1 -0
- package/lib/help-docs/17-connectors.md +241 -189
- package/lib/help-docs/21-build-connector.md +314 -0
- package/lib/help-docs/CLAUDE.md +4 -2
- package/lib/init.ts +25 -0
- package/lib/jobs/dispatcher.ts +28 -8
- package/lib/jobs/scheduler.ts +66 -6
- package/lib/jobs/store.ts +51 -2
- package/lib/jobs/types.ts +32 -0
- package/lib/pipeline-scheduler.ts +3 -2
- package/lib/pipeline.ts +137 -15
- package/lib/plugins/registry.ts +9 -42
- package/lib/plugins/types.ts +4 -129
- package/lib/settings.ts +7 -0
- package/lib/skills.ts +27 -1
- package/lib/task-manager.ts +62 -2
- package/package.json +4 -1
- package/src/core/db/database.ts +4 -0
- package/lib/builtin-plugins/github-api.yaml +0 -93
- package/lib/builtin-plugins/gitlab.yaml +0 -860
- package/lib/builtin-plugins/mantis.probe.js +0 -176
- package/lib/builtin-plugins/mantis.yaml +0 -964
- package/lib/builtin-plugins/pmdb.yaml +0 -178
- package/lib/builtin-plugins/teams.yaml +0 -913
|
@@ -1,16 +1,72 @@
|
|
|
1
1
|
# Connectors
|
|
2
2
|
|
|
3
|
-
**Connectors**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
>
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
|
|
3
|
+
**Connectors** expose external services (Mantis, GitLab, Teams, …) as
|
|
4
|
+
tools that the Forge chat agent and the browser extension can call.
|
|
5
|
+
Each connector is a declarative YAML manifest — schema + the extraction
|
|
6
|
+
scripts that implement each tool — pulled from the remote
|
|
7
|
+
[`forge-connectors`](https://github.com/aiwatching/forge-connectors)
|
|
8
|
+
registry.
|
|
9
|
+
|
|
10
|
+
> **Architecture**: connectors are an independent subsystem (`lib/connectors/`).
|
|
11
|
+
> They are NOT plugins. Pipeline-node plugins (`lib/plugins/`) and connectors
|
|
12
|
+
> share no schema, no registry, no config file.
|
|
13
|
+
|
|
14
|
+
There are **no built-in connectors**. A fresh install starts with an empty
|
|
15
|
+
connector list; the user picks what they want from the marketplace.
|
|
16
|
+
|
|
17
|
+
## Marketplace flow
|
|
18
|
+
|
|
19
|
+
1. On boot, Forge fetches `registry.json` from `connectorsRepoUrl`
|
|
20
|
+
(default `https://raw.githubusercontent.com/aiwatching/forge-connectors/main`)
|
|
21
|
+
and caches it at `<dataDir>/connectors/registry-cache.json`.
|
|
22
|
+
2. The Settings → Connectors panel reads the cache and lists every
|
|
23
|
+
available connector with its install state:
|
|
24
|
+
- **Available** — in registry, not installed
|
|
25
|
+
- **Installed v0.5.0** — manifest on disk, config row present
|
|
26
|
+
- **Update 0.5 → 0.6** — registry has newer version
|
|
27
|
+
3. Clicking **Install** fetches `<id>/manifest.yaml` from the registry,
|
|
28
|
+
writes it to `<dataDir>/connectors/<id>/manifest.yaml`, and adds a
|
|
29
|
+
row to `<dataDir>/connector-configs.json`.
|
|
30
|
+
4. The user fills in settings (host URL, PAT, etc.) via the existing
|
|
31
|
+
settings UI in the extension or `/api/connectors/<id>/settings`.
|
|
32
|
+
5. Chat tools become available the instant the manifest lands on disk —
|
|
33
|
+
no Forge restart.
|
|
34
|
+
|
|
35
|
+
Sync re-runs every hour and on manual **Refresh** in the Settings panel.
|
|
36
|
+
Network failures are silent — the cache from the last successful sync
|
|
37
|
+
keeps the marketplace usable offline.
|
|
38
|
+
|
|
39
|
+
## Repository layout
|
|
40
|
+
|
|
41
|
+
The `forge-connectors` repo (forkable via `connectorsRepoUrl`) is:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
registry.json
|
|
45
|
+
<id>/
|
|
46
|
+
manifest.yaml ← what Forge installs
|
|
47
|
+
README.md ← optional, surfaced in the marketplace
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`registry.json` lists every connector with id/name/version/icon/description
|
|
51
|
+
+ `min_forge_version`. Forge only consults the registry for the install
|
|
52
|
+
flow; once installed, `<dataDir>/connectors/<id>/manifest.yaml` is the
|
|
53
|
+
source of truth for that user.
|
|
54
|
+
|
|
55
|
+
## Local data layout
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
<dataDir>/
|
|
59
|
+
├── connectors/
|
|
60
|
+
│ ├── registry-cache.json last fetch from forge-connectors
|
|
61
|
+
│ ├── mantis/manifest.yaml installed manifest copy
|
|
62
|
+
│ ├── gitlab/manifest.yaml
|
|
63
|
+
│ └── …
|
|
64
|
+
└── connector-configs.json { "<id>": { config, installed_version, enabled } }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Secret fields (`type: secret`) in `config` are encrypted at rest with
|
|
68
|
+
AES-256-GCM via the existing key at `<dataDir>/.encrypt-key`. The on-wire
|
|
69
|
+
GET response masks them with bullets so plaintext never leaves the server.
|
|
14
70
|
|
|
15
71
|
## Execution model: DOM extraction, not REST
|
|
16
72
|
|
|
@@ -19,72 +75,56 @@ user's tabs and parse rendered HTML via `chrome.scripting.executeScript`.
|
|
|
19
75
|
This is the whole point of routing through a browser extension — reuse
|
|
20
76
|
the user's already-authenticated UI session, with zero token management.
|
|
21
77
|
|
|
22
|
-
Why this matters in practice:
|
|
23
|
-
|
|
24
78
|
| System | REST API needs | Browser DOM needs |
|
|
25
79
|
|---|---|---|
|
|
26
|
-
| MantisBT | Per-user API token
|
|
27
|
-
| GitLab | Personal Access Token | Session cookie on `/-/issues/` etc. ✅ |
|
|
80
|
+
| MantisBT | Per-user API token | Already-logged-in PHPSESSID cookie ✅ |
|
|
81
|
+
| GitLab UI | Personal Access Token | Session cookie on `/-/issues/` etc. ✅ |
|
|
28
82
|
| JIRA Server | PAT or basic auth | Session cookie ✅ |
|
|
29
83
|
| Teams web | MSAL bearer token | Session cookie ✅ |
|
|
30
84
|
|
|
31
|
-
If a connector hit the REST API path, every user would need to mint and
|
|
32
|
-
manage a token per system. That defeats the extension's value
|
|
33
|
-
proposition. **Token-based fallback is opt-in per connector, not the
|
|
34
|
-
default code path.**
|
|
35
|
-
|
|
36
85
|
This means:
|
|
37
|
-
- **No PAT / API tokens to manage** — connector reuses the user's logged-in browser session.
|
|
38
|
-
- **Data never flows through Forge** — Forge only ships the manifest + extraction script. The LLM in the extension calls the tool, the extension scrapes in the user's tab, the LLM sees the result.
|
|
39
|
-
- Forge's role: **discovery + settings sync + script delivery**.
|
|
40
|
-
- Trade-off: site redesigns break scripts. Bump the manifest `version` and update the `script` block — users get the fix on next refresh, no extension release.
|
|
41
86
|
|
|
42
|
-
|
|
87
|
+
- **No PAT / API tokens to manage** for browser connectors — they reuse
|
|
88
|
+
the user's logged-in session.
|
|
89
|
+
- **Data never flows through Forge** — Forge only ships the manifest +
|
|
90
|
+
extraction script. The LLM in the chat backend calls the tool, the
|
|
91
|
+
extension scrapes in the user's tab, the LLM sees the result.
|
|
92
|
+
- Trade-off: site redesigns break scripts. Bump the manifest `version`
|
|
93
|
+
in the registry and users pull the fix on next sync — no Forge or
|
|
94
|
+
extension release needed.
|
|
43
95
|
|
|
44
|
-
|
|
45
|
-
Forge Extension User's tabs
|
|
46
|
-
───── ───────── ───────────
|
|
47
|
-
mantis.yaml ┌──────────────┐
|
|
48
|
-
tools: Generic runner │ Mantis tab │
|
|
49
|
-
list_my_bugs: ─────────▶ ① fetch manifest │ (logged in) │
|
|
50
|
-
page: { url, ... } ② render templates │ │
|
|
51
|
-
script: | ③ acquire tab matching │ #bug_list │
|
|
52
|
-
const list = ... host_match, navigate │ ◀── runs │
|
|
53
|
-
return { bugs, ... } to page.url the script
|
|
54
|
-
④ executeScript(script) from the
|
|
55
|
-
in the tab manifest
|
|
56
|
-
⑤ return JSON to LLM │ │
|
|
57
|
-
└──────────────┘
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
The extension has **no per-connector code**. Adding GitLab means dropping
|
|
61
|
-
a `gitlab.yaml` next to `mantis.yaml` — extension auto-discovers it.
|
|
62
|
-
|
|
63
|
-
## Plugin manifest format
|
|
64
|
-
|
|
65
|
-
A connector plugin is a YAML in `lib/builtin-plugins/<id>.yaml` (or
|
|
66
|
-
`~/.forge/plugins/<id>/plugin.yaml`):
|
|
96
|
+
## Manifest format
|
|
67
97
|
|
|
68
98
|
```yaml
|
|
69
99
|
id: mantis
|
|
70
100
|
name: MantisBT
|
|
71
101
|
icon: "🐞"
|
|
72
|
-
version: "0.
|
|
73
|
-
|
|
74
|
-
|
|
102
|
+
version: "0.5.0"
|
|
103
|
+
author: forge
|
|
104
|
+
description: |
|
|
105
|
+
Read + comment on MantisBT bugs from the user's logged-in browser session.
|
|
75
106
|
|
|
76
|
-
|
|
107
|
+
min_forge_version: "0.8.0"
|
|
108
|
+
|
|
109
|
+
# Default extension execution context.
|
|
110
|
+
# main — permissive-CSP sites (Mantis, GitLab self-hosted)
|
|
111
|
+
# isolated — strict-CSP sites that block eval (Teams, github.com)
|
|
112
|
+
runner: main
|
|
113
|
+
|
|
114
|
+
# Per-user settings (rendered as a form in the extension).
|
|
77
115
|
settings:
|
|
78
116
|
base_url:
|
|
79
117
|
type: string
|
|
80
118
|
label: Mantis base URL
|
|
81
119
|
required: true
|
|
120
|
+
token:
|
|
121
|
+
type: secret # encrypted at rest (AES-256-GCM)
|
|
122
|
+
label: API token (optional fallback)
|
|
82
123
|
|
|
83
|
-
#
|
|
84
|
-
# Chrome match pattern; {settings.*} expanded at API response time.
|
|
124
|
+
# Chrome match pattern; {settings.*} expanded server-side.
|
|
85
125
|
host_match: "{base_url}/*"
|
|
86
126
|
|
|
87
|
-
# Substring detected after navigation →
|
|
127
|
+
# Substring detected after navigation → "user not logged in".
|
|
88
128
|
login_redirect: "/login_page.php"
|
|
89
129
|
|
|
90
130
|
tools:
|
|
@@ -94,22 +134,19 @@ tools:
|
|
|
94
134
|
status: { type: select, options: ["open", "closed", "all"], default: "open" }
|
|
95
135
|
limit: { type: number, default: 50 }
|
|
96
136
|
|
|
97
|
-
#
|
|
137
|
+
# Default protocol is 'browser' — runs in the user's tab.
|
|
98
138
|
page:
|
|
99
139
|
url: "{base_url}/view_all_bug_page.php"
|
|
100
140
|
on_target: "/view_all_bug_page.php"
|
|
101
|
-
|
|
102
|
-
# Function BODY. Implicit `args` parameter. Returns JSON-serializable.
|
|
103
|
-
# Runs IN THE USER'S TAB (page context). No closures over Forge / extension.
|
|
104
141
|
script: |
|
|
105
142
|
const { status, limit } = args;
|
|
106
143
|
const list = document.querySelector('#bug_list');
|
|
107
144
|
if (!list) return { bugs: [], total: 0, _error: '#bug_list not found' };
|
|
108
|
-
//
|
|
145
|
+
// …extract rows, filter, return
|
|
109
146
|
return { bugs, total };
|
|
110
147
|
|
|
111
148
|
add_comment:
|
|
112
|
-
destructive: true
|
|
149
|
+
destructive: true # extension prompts before running
|
|
113
150
|
description: "Add a note to a bug."
|
|
114
151
|
parameters:
|
|
115
152
|
bug_id: { type: number, required: true }
|
|
@@ -118,78 +155,104 @@ tools:
|
|
|
118
155
|
url: "{base_url}/bug_view_page.php?bug_id={args.bug_id}"
|
|
119
156
|
script: |
|
|
120
157
|
// Form-submit or fetch() with same-origin cookies
|
|
121
|
-
|
|
158
|
+
…
|
|
122
159
|
```
|
|
123
160
|
|
|
124
161
|
**Template variables**:
|
|
125
162
|
- `{base_url}` / `{settings.<name>}` → expanded server-side from saved settings
|
|
126
|
-
- `{args.<name>}` → expanded by the
|
|
163
|
+
- `{args.<name>}` → expanded by the runtime from the LLM's tool input
|
|
127
164
|
|
|
128
|
-
|
|
165
|
+
**`script` contract**:
|
|
129
166
|
- Receives `args` (the LLM's parameters)
|
|
130
167
|
- Returns a JSON-serializable value (no DOM nodes, no functions)
|
|
131
168
|
- Has access to `document`, `fetch`, `URL`, etc. (page context)
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
169
|
+
- Same-origin `fetch()` — cookies auto-attached
|
|
170
|
+
- Self-contained — no closures over the manifest's surroundings
|
|
171
|
+
- Uncaught throws become tool errors
|
|
135
172
|
|
|
136
|
-
|
|
173
|
+
## Server-side protocols (`http`, `shell`)
|
|
137
174
|
|
|
138
|
-
|
|
175
|
+
When a service has a clean REST API and you don't need a browser tab at
|
|
176
|
+
all, declare `protocol: http` on the tool. Forge issues the request
|
|
177
|
+
server-side and returns the body to the LLM. Same for `protocol: shell`
|
|
178
|
+
— Forge spawns a process with an explicit arg array (no `shell:true`,
|
|
179
|
+
so templated values cannot inject metacharacters).
|
|
139
180
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
181
|
+
```yaml
|
|
182
|
+
tools:
|
|
183
|
+
get_repo:
|
|
184
|
+
protocol: http
|
|
185
|
+
parameters:
|
|
186
|
+
repo: { type: string, required: true }
|
|
187
|
+
request:
|
|
188
|
+
method: GET
|
|
189
|
+
url: 'https://api.github.com/repos/{args.repo}'
|
|
190
|
+
headers:
|
|
191
|
+
Accept: 'application/vnd.github+json'
|
|
192
|
+
Authorization: 'Bearer {settings.token}'
|
|
193
|
+
|
|
194
|
+
git_log:
|
|
195
|
+
protocol: shell
|
|
196
|
+
parameters:
|
|
197
|
+
repo: { type: string, required: true }
|
|
198
|
+
n: { type: number }
|
|
199
|
+
command: ['git', '-C', '{args.repo}', 'log', '-n', '{args.n}', '--oneline']
|
|
200
|
+
timeout_ms: 5000
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Response body / stdout is truncated to ~8 KB for the LLM context;
|
|
204
|
+
default timeout 30 s, max 5 min. `protocol: browser` (the default) runs
|
|
205
|
+
in the extension; `http` and `shell` run entirely on Forge (chat-standalone,
|
|
206
|
+
port 8408).
|
|
207
|
+
|
|
208
|
+
**Safety:** `protocol: shell` lets a manifest invoke any binary on PATH.
|
|
209
|
+
Always read the script before installing.
|
|
210
|
+
|
|
211
|
+
## 1:1 vs 1:N suites
|
|
212
|
+
|
|
213
|
+
The default is **one connector = one tool surface** (Mantis, GitLab,
|
|
214
|
+
Teams, …). For same-vendor suites with shared auth (Atlassian, Google
|
|
215
|
+
Workspace, M365), a manifest can declare multiple `connectors[]`
|
|
216
|
+
entries that share user settings:
|
|
144
217
|
|
|
145
218
|
```yaml
|
|
146
219
|
id: atlassian-suite
|
|
147
|
-
category: connector
|
|
148
|
-
mode: hybrid
|
|
149
220
|
connectors:
|
|
150
221
|
- id: jira
|
|
151
222
|
host_match: "*://*.atlassian.net/*"
|
|
152
|
-
tools: {
|
|
223
|
+
tools: { … }
|
|
153
224
|
- id: confluence
|
|
154
225
|
host_match: "*://*.atlassian.net/wiki/*"
|
|
155
|
-
tools: {
|
|
226
|
+
tools: { … }
|
|
156
227
|
```
|
|
157
228
|
|
|
158
|
-
Most
|
|
229
|
+
Most manifests should stay 1:1. Use 1:N only for genuine shared-auth suites.
|
|
159
230
|
|
|
160
231
|
## HTTP API
|
|
161
232
|
|
|
162
|
-
All endpoints require `X-Forge-Token`
|
|
233
|
+
All endpoints require `X-Forge-Token` (obtain via `POST /api/auth/verify`).
|
|
163
234
|
|
|
164
235
|
### `GET /api/connectors`
|
|
165
|
-
List
|
|
166
|
-
|
|
167
|
-
from the user's saved settings; `{args.*}` is left literal.
|
|
236
|
+
List installed connectors. `page.url` / `host_match` etc. are expanded
|
|
237
|
+
from the user's settings; `{args.*}` stays literal for the runtime.
|
|
168
238
|
|
|
169
239
|
```json
|
|
170
240
|
{
|
|
171
241
|
"connectors": [
|
|
172
242
|
{
|
|
173
|
-
"plugin_id": "mantis",
|
|
243
|
+
"plugin_id": "mantis", /* legacy wire-name; equals id */
|
|
174
244
|
"name": "MantisBT",
|
|
175
245
|
"icon": "🐞",
|
|
176
|
-
"version": "0.
|
|
177
|
-
"mode": "browser-side",
|
|
246
|
+
"version": "0.5.0",
|
|
178
247
|
"installed": true,
|
|
179
248
|
"host_match": "https://mantis.acme.com/*",
|
|
180
249
|
"login_redirect": "/login_page.php",
|
|
250
|
+
"runner": "main",
|
|
181
251
|
"entries": [
|
|
182
252
|
{
|
|
183
253
|
"id": "mantis",
|
|
184
|
-
"tools": {
|
|
185
|
-
|
|
186
|
-
"description": "...",
|
|
187
|
-
"parameters": {...},
|
|
188
|
-
"page": { "url": "https://mantis.acme.com/view_all_bug_page.php", "on_target": "/view_all_bug_page.php" },
|
|
189
|
-
"script": "const { status, limit } = args; ..."
|
|
190
|
-
}
|
|
191
|
-
},
|
|
192
|
-
"settings": { "base_url": {...} }
|
|
254
|
+
"tools": { … },
|
|
255
|
+
"settings": { "base_url": { … } }
|
|
193
256
|
}
|
|
194
257
|
]
|
|
195
258
|
}
|
|
@@ -197,126 +260,115 @@ from the user's saved settings; `{args.*}` is left literal.
|
|
|
197
260
|
}
|
|
198
261
|
```
|
|
199
262
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
- `id=<plugin_id>` → single connector detail (alternative to a path param)
|
|
203
|
-
|
|
204
|
-
### `GET /api/connectors?id=<id>` / `GET /api/connectors/<id>`
|
|
205
|
-
Single connector detail. Same shape as a list entry, wrapped in `{ connector: ... }`.
|
|
263
|
+
### `GET /api/connectors?id=<id>`
|
|
264
|
+
Single connector detail, wrapped in `{ connector: … }`.
|
|
206
265
|
|
|
207
266
|
### `GET /api/connectors/<id>/settings`
|
|
208
|
-
Read the user's saved settings
|
|
267
|
+
Read the user's saved settings. Secret fields come back as bullets.
|
|
209
268
|
|
|
210
269
|
```json
|
|
211
270
|
{
|
|
212
|
-
"settings": { "base_url": "https://mantis.acme.com", "
|
|
213
|
-
"schema":
|
|
271
|
+
"settings": { "base_url": "https://mantis.acme.com", "token": "••••••••" },
|
|
272
|
+
"schema": { "base_url": { … }, "token": { "type": "secret", … } },
|
|
214
273
|
"installed": true
|
|
215
274
|
}
|
|
216
275
|
```
|
|
217
276
|
|
|
218
|
-
`schema` is the merged settings schema across all entries (1:N). `settings`
|
|
219
|
-
includes any defaults declared in the schema, overlaid with the user's
|
|
220
|
-
saved values.
|
|
221
|
-
|
|
222
277
|
### `POST /api/connectors/<id>/settings`
|
|
223
|
-
Save settings.
|
|
278
|
+
Save settings. If the client sends `••••••••` for a secret, the stored
|
|
279
|
+
value is preserved (so editing one field doesn't wipe the PAT).
|
|
224
280
|
|
|
225
|
-
|
|
281
|
+
### `GET /api/connectors/marketplace`
|
|
282
|
+
List registry entries merged with installed state.
|
|
226
283
|
|
|
227
284
|
```json
|
|
228
|
-
{
|
|
285
|
+
{
|
|
286
|
+
"fetched_at": "2026-05-19T16:31:02.000Z",
|
|
287
|
+
"base_url": "https://raw.githubusercontent.com/aiwatching/forge-connectors/main",
|
|
288
|
+
"entries": [
|
|
289
|
+
{ "id": "mantis", "version": "0.5.0", "installed_version": "0.5.0", "update_available": false, … },
|
|
290
|
+
{ "id": "gitlab", "version": "1.1.0", "installed_version": "1.0.0", "update_available": true, … },
|
|
291
|
+
{ "id": "teams", "version": "0.8.1", /* available */ … }
|
|
292
|
+
]
|
|
293
|
+
}
|
|
229
294
|
```
|
|
230
295
|
|
|
231
|
-
|
|
296
|
+
### `POST /api/connectors/marketplace`
|
|
297
|
+
Marketplace actions.
|
|
232
298
|
|
|
233
|
-
|
|
|
299
|
+
| `action` | Effect |
|
|
234
300
|
|---|---|
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
# lib/builtin-plugins/github-api.yaml — see file for full example
|
|
273
|
-
tools:
|
|
274
|
-
get_repo:
|
|
275
|
-
protocol: http
|
|
276
|
-
parameters:
|
|
277
|
-
repo: { type: string, required: true }
|
|
278
|
-
request:
|
|
279
|
-
method: GET
|
|
280
|
-
url: 'https://api.github.com/repos/{args.repo}'
|
|
281
|
-
headers:
|
|
282
|
-
Accept: 'application/vnd.github+json'
|
|
283
|
-
Authorization: 'Bearer {settings.token}'
|
|
284
|
-
|
|
285
|
-
git_log:
|
|
286
|
-
protocol: shell
|
|
287
|
-
parameters:
|
|
288
|
-
repo: { type: string, required: true }
|
|
289
|
-
n: { type: number }
|
|
290
|
-
command: ['git', '-C', '{args.repo}', 'log', '-n', '{args.n}', '--oneline']
|
|
291
|
-
timeout_ms: 5000
|
|
301
|
+
| `sync` | Re-fetch `registry.json` (and refresh installed manifests with `refreshInstalled: true`) |
|
|
302
|
+
| `install` | Fetch the manifest for `id` and write it to `<dataDir>/connectors/<id>/` |
|
|
303
|
+
| `update` | Alias for `install` — pulls the latest manifest |
|
|
304
|
+
| `uninstall` | Drop the manifest. `keepConfig: true` (default) preserves the user's settings + PAT |
|
|
305
|
+
| `status` | Probe a single `id`: returns `{ installed, installed_version, enabled }` |
|
|
306
|
+
|
|
307
|
+
## Authoring a new connector
|
|
308
|
+
|
|
309
|
+
Three ways to land a custom connector:
|
|
310
|
+
|
|
311
|
+
1. **Local-only (fastest)** — write a `manifest.yaml`, upload it via
|
|
312
|
+
the "+ Upload" button in Settings → Connectors (or drag-drop). For
|
|
313
|
+
multi-file connectors, zip it with `manifest.yaml` at the root.
|
|
314
|
+
See `21-build-connector.md` for the manifest schema and an
|
|
315
|
+
interview-style template.
|
|
316
|
+
2. **Forge Help AI** — ask Forge chat "build me a connector for X"; the
|
|
317
|
+
AI walks through requirements, writes the manifest to
|
|
318
|
+
`<dataDir>/connectors/<id>/manifest.yaml`, and registers it via
|
|
319
|
+
`POST /api/connectors/install-local`.
|
|
320
|
+
3. **Share via forge-connectors** — fork
|
|
321
|
+
[`forge-connectors`](https://github.com/aiwatching/forge-connectors),
|
|
322
|
+
add `<id>/manifest.yaml`, add an entry to `registry.json`, open a PR.
|
|
323
|
+
Maintainers review the script bodies before merge.
|
|
324
|
+
|
|
325
|
+
Iterating on a private registry: point `settings.connectorsRepoUrl` at
|
|
326
|
+
a private fork.
|
|
327
|
+
|
|
328
|
+
### POST /api/connectors/install-local
|
|
329
|
+
|
|
330
|
+
Used by both the Upload button and the Help AI:
|
|
331
|
+
|
|
332
|
+
```http
|
|
333
|
+
POST /api/connectors/install-local
|
|
334
|
+
Content-Type: application/json
|
|
335
|
+
X-Forge-Token: …
|
|
336
|
+
|
|
337
|
+
{ "yaml": "id: my-jira\nname: MyJira\nversion: \"0.1.0\"\n..." }
|
|
292
338
|
```
|
|
293
339
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
`shell`, every arg is templated independently so an arg with spaces or
|
|
297
|
-
quotes stays a single literal arg.
|
|
340
|
+
or `multipart/form-data` with a `file` field accepting `.yaml`,
|
|
341
|
+
`.yml`, or `.zip`. Zip must have `manifest.yaml` at the root.
|
|
298
342
|
|
|
299
|
-
|
|
300
|
-
default timeout 30 s, max 5 min. `protocol: browser` (the default)
|
|
301
|
-
keeps using the extension runner; `http` and `shell` run entirely on
|
|
302
|
-
Forge (chat-standalone, port 8408).
|
|
343
|
+
## Migration from pre-v0.9
|
|
303
344
|
|
|
304
|
-
|
|
305
|
-
|
|
345
|
+
Pre-v0.9 Forge stored connectors as built-in plugins under
|
|
346
|
+
`lib/builtin-plugins/<id>.yaml` with `category: connector`. On upgrade:
|
|
306
347
|
|
|
307
|
-
|
|
348
|
+
1. `migrateConnectorConfigs()` runs once per boot. For each known id
|
|
349
|
+
(mantis, gitlab, teams, pmdb, github-api), if there's a row in
|
|
350
|
+
`plugin-configs.json`, the encrypted config blob is moved to
|
|
351
|
+
`connector-configs.json` and the old row is removed.
|
|
352
|
+
2. The next `syncRegistry({ refreshInstalled: true })` pulls the
|
|
353
|
+
matching manifest from `forge-connectors` and writes it to
|
|
354
|
+
`<dataDir>/connectors/<id>/manifest.yaml`.
|
|
355
|
+
3. Chat tools come back online ~1 s after migration completes. PAT
|
|
356
|
+
and base URL are preserved across the upgrade.
|
|
308
357
|
|
|
309
|
-
If
|
|
310
|
-
|
|
311
|
-
|
|
358
|
+
If the user is offline at upgrade time, the marketplace will be empty
|
|
359
|
+
until the network returns; config rows remain, so the next sync
|
|
360
|
+
restores everything without re-entering credentials.
|
|
312
361
|
|
|
313
362
|
## Troubleshooting
|
|
314
363
|
|
|
315
364
|
| Symptom | Cause + fix |
|
|
316
365
|
|---|---|
|
|
317
|
-
|
|
|
318
|
-
|
|
|
319
|
-
|
|
|
320
|
-
|
|
|
321
|
-
|
|
|
322
|
-
|
|
|
366
|
+
| Marketplace is empty | Initial sync failed (offline / bad URL). Click **Refresh** in Settings → Connectors. |
|
|
367
|
+
| `Sync error: … self-signed certificate in certificate chain` | Corporate TLS-interception proxy (Zscaler, Palo Alto, etc.). Node doesn't trust the proxy's root CA by default. Export `NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem` before starting Forge, or run `forge server restart` with that env var set. macOS users can extract the bundle from Keychain Access → System Roots. |
|
|
368
|
+
| `Sync error: … ENOTFOUND` | DNS can't resolve `raw.githubusercontent.com` — VPN / firewall blocking. Point `connectorsRepoUrl` at a private mirror, or sync manifests manually into `<dataDir>/connectors/<id>/manifest.yaml`. |
|
|
369
|
+
| Installed connector missing from chat | Manifest deleted but config row remains. Re-install from the marketplace. |
|
|
370
|
+
| "Update available" badge stuck | Network failure on the refresh. Re-sync via **Refresh**. |
|
|
371
|
+
| Tool not appearing for the LLM | Connector installed but disabled. Toggle it on in the Settings panel. |
|
|
372
|
+
| Tool returns "login required" | `login_redirect` substring matched the tab URL after nav — user needs to log in to that site. |
|
|
373
|
+
| Strict-CSP site refuses `new Function` | Page CSP blocks dynamic eval. Set `runner: isolated` in the manifest, or rewrite the tool as `protocol: http`. |
|
|
374
|
+
| `POST /api/connectors/<id>/settings` returns 404 | Manifest not on disk (`<dataDir>/connectors/<id>/manifest.yaml`). Install the connector first. |
|