@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.
Files changed (45) hide show
  1. package/RELEASE_NOTES.md +25 -6
  2. package/app/api/connectors/[id]/settings/route.ts +31 -37
  3. package/app/api/connectors/[id]/test/route.ts +260 -0
  4. package/app/api/connectors/install-local/route.ts +211 -0
  5. package/app/api/connectors/marketplace/route.ts +79 -0
  6. package/app/api/connectors/route.ts +41 -46
  7. package/app/api/jobs/route.ts +1 -0
  8. package/app/api/skills/install-local/route.ts +282 -0
  9. package/components/ConnectorsPanel.tsx +526 -211
  10. package/components/SettingsModal.tsx +1 -0
  11. package/components/SkillsPanel.tsx +42 -1
  12. package/lib/agents/claude-adapter.ts +4 -0
  13. package/lib/agents/types.ts +6 -0
  14. package/lib/chat/agent-loop.ts +13 -22
  15. package/lib/chat/protocols/http.ts +1 -1
  16. package/lib/chat/protocols/shell.ts +1 -1
  17. package/lib/chat/tool-dispatcher.ts +20 -20
  18. package/lib/connectors/migration.ts +110 -0
  19. package/lib/connectors/registry.ts +328 -0
  20. package/lib/connectors/sync.ts +305 -0
  21. package/lib/connectors/types.ts +253 -0
  22. package/lib/help-docs/00-overview.md +1 -0
  23. package/lib/help-docs/17-connectors.md +241 -189
  24. package/lib/help-docs/21-build-connector.md +314 -0
  25. package/lib/help-docs/CLAUDE.md +4 -2
  26. package/lib/init.ts +25 -0
  27. package/lib/jobs/dispatcher.ts +28 -8
  28. package/lib/jobs/scheduler.ts +21 -3
  29. package/lib/jobs/store.ts +11 -2
  30. package/lib/jobs/types.ts +12 -0
  31. package/lib/pipeline-scheduler.ts +3 -2
  32. package/lib/pipeline.ts +135 -13
  33. package/lib/plugins/registry.ts +9 -42
  34. package/lib/plugins/types.ts +4 -129
  35. package/lib/settings.ts +7 -0
  36. package/lib/skills.ts +27 -1
  37. package/lib/task-manager.ts +62 -2
  38. package/package.json +3 -1
  39. package/src/core/db/database.ts +4 -0
  40. package/lib/builtin-plugins/github-api.yaml +0 -93
  41. package/lib/builtin-plugins/gitlab.yaml +0 -860
  42. package/lib/builtin-plugins/mantis.probe.js +0 -176
  43. package/lib/builtin-plugins/mantis.yaml +0 -964
  44. package/lib/builtin-plugins/pmdb.yaml +0 -178
  45. 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
- };