@aion0/forge 0.8.1 → 0.8.2
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 +25 -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 +1 -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 +21 -3
- package/lib/jobs/store.ts +11 -2
- package/lib/jobs/types.ts +12 -0
- package/lib/pipeline-scheduler.ts +3 -2
- package/lib/pipeline.ts +135 -13
- 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 +3 -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,178 +0,0 @@
|
|
|
1
|
-
id: pmdb
|
|
2
|
-
name: PMDB
|
|
3
|
-
icon: "🏭"
|
|
4
|
-
version: "0.2.0"
|
|
5
|
-
author: forge
|
|
6
|
-
category: connector
|
|
7
|
-
mode: browser-side
|
|
8
|
-
description: |
|
|
9
|
-
Forge PMDB (Product Management Database) — read-only DOM extraction
|
|
10
|
-
from the user's logged-in browser session.
|
|
11
|
-
|
|
12
|
-
v0.2.0 covers Hardware PRDs only: list with optional client-side filter
|
|
13
|
-
and single-PRD detail extraction (View tab metadata + revision history).
|
|
14
|
-
Other sections (Software, Datasheet, QSG, Roadmap, PSIRT) can be added
|
|
15
|
-
incrementally as needed.
|
|
16
|
-
|
|
17
|
-
Selectors verified on https://pmdb.Forge.com/Hardware/list and
|
|
18
|
-
/Hardware/PRD/1871 via chrome-devtools-mcp on 2026-05-16.
|
|
19
|
-
|
|
20
|
-
settings:
|
|
21
|
-
base_url:
|
|
22
|
-
type: string
|
|
23
|
-
label: PMDB base URL
|
|
24
|
-
description: "Forge internal PMDB host"
|
|
25
|
-
default: "https://pmdb.Forge.com"
|
|
26
|
-
required: true
|
|
27
|
-
|
|
28
|
-
host_match: "{base_url}/*"
|
|
29
|
-
login_redirect: "/login"
|
|
30
|
-
|
|
31
|
-
tools:
|
|
32
|
-
list_prds:
|
|
33
|
-
description: |
|
|
34
|
-
List Hardware PRDs (Product Requirement Documents). Returns
|
|
35
|
-
product / status / LPA status / detail URL / PRD id. Supports
|
|
36
|
-
optional client-side substring filter on product name + status.
|
|
37
|
-
parameters:
|
|
38
|
-
query:
|
|
39
|
-
type: string
|
|
40
|
-
label: Substring to match against product name (case-insensitive)
|
|
41
|
-
required: false
|
|
42
|
-
status:
|
|
43
|
-
type: string
|
|
44
|
-
label: Status filter (e.g. Active, EOL — exact match, case-insensitive)
|
|
45
|
-
required: false
|
|
46
|
-
limit:
|
|
47
|
-
type: number
|
|
48
|
-
default: 50
|
|
49
|
-
returns: "{ prds: [...], total, filtered }"
|
|
50
|
-
page:
|
|
51
|
-
url: "{base_url}/Hardware/list"
|
|
52
|
-
on_target: "/Hardware/list"
|
|
53
|
-
script: |
|
|
54
|
-
const tables = Array.from(document.querySelectorAll('table'));
|
|
55
|
-
const mainTable = tables.reduce((best, t) => {
|
|
56
|
-
const rows = t.querySelectorAll('tr').length;
|
|
57
|
-
return (!best || rows > best.querySelectorAll('tr').length) ? t : best;
|
|
58
|
-
}, null);
|
|
59
|
-
if (!mainTable) {
|
|
60
|
-
return { prds: [], total: 0, _error: 'PRD table not found on ' + location.pathname };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const headerRow = mainTable.querySelector('thead tr') || mainTable.querySelector('tr');
|
|
64
|
-
const headers = Array.from(headerRow?.querySelectorAll('th, td') || []).map(c => c.textContent.trim().toLowerCase());
|
|
65
|
-
const colIdx = (name) => headers.findIndex(h => h === name.toLowerCase() || h.includes(name.toLowerCase()));
|
|
66
|
-
|
|
67
|
-
const productIdx = colIdx('product');
|
|
68
|
-
const statusIdx = colIdx('status');
|
|
69
|
-
const lpaIdx = colIdx('lpa');
|
|
70
|
-
|
|
71
|
-
const dataRows = Array.from(mainTable.querySelectorAll('tbody tr'));
|
|
72
|
-
const all = dataRows.map(r => {
|
|
73
|
-
const cells = Array.from(r.querySelectorAll('td'));
|
|
74
|
-
const link = r.querySelector('a[href*="/Hardware/PRD/"]');
|
|
75
|
-
const href = link?.getAttribute('href') || '';
|
|
76
|
-
const idMatch = href.match(/\/Hardware\/PRD\/(\d+)/);
|
|
77
|
-
const product = productIdx >= 0 ? cells[productIdx]?.textContent.trim() : '';
|
|
78
|
-
const status = statusIdx >= 0 ? cells[statusIdx]?.textContent.trim() : '';
|
|
79
|
-
const lpa = lpaIdx >= 0 ? cells[lpaIdx]?.textContent.trim() : '';
|
|
80
|
-
return {
|
|
81
|
-
prd_id: idMatch ? Number(idMatch[1]) : 0,
|
|
82
|
-
product,
|
|
83
|
-
status,
|
|
84
|
-
lpa_status: lpa,
|
|
85
|
-
url: link?.href || '',
|
|
86
|
-
};
|
|
87
|
-
}).filter(p => p.prd_id);
|
|
88
|
-
|
|
89
|
-
const q = (args.query || '').toLowerCase().trim();
|
|
90
|
-
const s = (args.status || '').toLowerCase().trim();
|
|
91
|
-
const filtered = all.filter(p => {
|
|
92
|
-
if (q && !p.product.toLowerCase().includes(q)) return false;
|
|
93
|
-
if (s && p.status.toLowerCase() !== s) return false;
|
|
94
|
-
return true;
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
prds: filtered.slice(0, args.limit || 50),
|
|
99
|
-
total: all.length,
|
|
100
|
-
filtered: filtered.length,
|
|
101
|
-
_page: location.href,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
get_prd:
|
|
105
|
-
description: |
|
|
106
|
-
Get full details for a single Hardware PRD by ID. Returns metadata
|
|
107
|
-
from the View tab (priority, assignee, status, ETA, manager, group,
|
|
108
|
-
project families, etc.) plus the most recent revision entry.
|
|
109
|
-
parameters:
|
|
110
|
-
prd_id:
|
|
111
|
-
type: number
|
|
112
|
-
label: PRD ID (numeric, e.g. 1871)
|
|
113
|
-
required: true
|
|
114
|
-
returns: "{ prd_id, title, fields: {...}, recent_revisions: [...], tabs: [...] }"
|
|
115
|
-
page:
|
|
116
|
-
url: "{base_url}/Hardware/PRD/{args.prd_id}"
|
|
117
|
-
on_target: "/Hardware/PRD/{args.prd_id}"
|
|
118
|
-
script: |
|
|
119
|
-
// Title is in the page heading: "FIM-7921F (Purchase Request ID: PRID1001871)"
|
|
120
|
-
const titleEl = document.querySelector('h1, h2.page-header, .page-header h1, .page-header h2');
|
|
121
|
-
const title = titleEl?.textContent?.trim() || document.title.replace(/^PMDB \| /, '');
|
|
122
|
-
|
|
123
|
-
// Tabs list — useful for caller to know what else is available
|
|
124
|
-
const tabs = Array.from(document.querySelectorAll('[role="tab"], .nav-tabs a, ul.nav-tabs li a'))
|
|
125
|
-
.map(t => t.textContent.trim())
|
|
126
|
-
.filter(Boolean);
|
|
127
|
-
|
|
128
|
-
// Walk label → value pairs from form-group / row layout. PMDB is a
|
|
129
|
-
// mix of Bootstrap form-group and table-based field layouts; collect
|
|
130
|
-
// both and de-dup by label.
|
|
131
|
-
const fields = {};
|
|
132
|
-
const containers = Array.from(document.querySelectorAll('.form-group, .row'));
|
|
133
|
-
containers.forEach(g => {
|
|
134
|
-
const label = g.querySelector('label, .control-label, .field-label');
|
|
135
|
-
if (!label) return;
|
|
136
|
-
const labelText = label.textContent.trim();
|
|
137
|
-
if (!labelText || fields[labelText]) return;
|
|
138
|
-
const valueCandidates = [
|
|
139
|
-
g.querySelector('.form-control-static'),
|
|
140
|
-
g.querySelector('input[type="text"]'),
|
|
141
|
-
g.querySelector('input[type="hidden"]'),
|
|
142
|
-
g.querySelector('select option[selected]'),
|
|
143
|
-
g.querySelector('textarea'),
|
|
144
|
-
g.querySelector('.value, .field-value'),
|
|
145
|
-
g.querySelector('td:not(:first-child)'),
|
|
146
|
-
g.querySelector('p, span:not(.label):not(.control-label):not(.help-block)'),
|
|
147
|
-
].filter(Boolean);
|
|
148
|
-
const v = valueCandidates[0];
|
|
149
|
-
if (!v) return;
|
|
150
|
-
let value = '';
|
|
151
|
-
if (v.tagName === 'INPUT' || v.tagName === 'TEXTAREA') value = v.value;
|
|
152
|
-
else value = v.textContent?.trim() || '';
|
|
153
|
-
if (value) fields[labelText] = value.slice(0, 500);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Revision history — typically the first table on the View tab
|
|
157
|
-
const revTable = Array.from(document.querySelectorAll('table'))
|
|
158
|
-
.find(t => /revision/i.test(t.previousElementSibling?.textContent || '') ||
|
|
159
|
-
/revision/i.test(t.querySelector('th')?.textContent || ''));
|
|
160
|
-
const recent_revisions = revTable
|
|
161
|
-
? Array.from(revTable.querySelectorAll('tbody tr')).slice(0, 5).map(r => {
|
|
162
|
-
const c = Array.from(r.querySelectorAll('td')).map(x => x.textContent.trim());
|
|
163
|
-
return { revision: c[0] || '', date: c[1] || '', author: c[2] || '', comments: c[3]?.slice(0, 200) || '' };
|
|
164
|
-
})
|
|
165
|
-
: [];
|
|
166
|
-
|
|
167
|
-
// Extract PRID from heading for cross-reference
|
|
168
|
-
const pridMatch = title.match(/PRID(\d+)/i);
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
prd_id: args.prd_id,
|
|
172
|
-
prid: pridMatch ? 'PRID' + pridMatch[1] : '',
|
|
173
|
-
title,
|
|
174
|
-
fields,
|
|
175
|
-
recent_revisions,
|
|
176
|
-
tabs,
|
|
177
|
-
url: location.href,
|
|
178
|
-
};
|