@aion0/forge 0.5.43 → 0.5.44
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 +14 -5
- package/components/Dashboard.tsx +14 -0
- package/intellij-plugin/README.md +53 -0
- package/intellij-plugin/build.gradle.kts +60 -0
- package/intellij-plugin/gradle/gradle-daemon-jvm.properties +12 -0
- package/intellij-plugin/gradle.properties +9 -0
- package/intellij-plugin/publish.sh +78 -0
- package/intellij-plugin/settings.gradle.kts +7 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/action/LoginAction.kt +49 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/action/LogoutAction.kt +18 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/action/OpenWebUIAction.kt +13 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/action/SwitchConnectionAction.kt +26 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/api/ForgeClient.kt +115 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/auth/Auth.kt +31 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/connection/ConnectionManager.kt +95 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/settings/ForgeConfigurable.kt +81 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/DocsView.kt +99 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/ForgeStatusBarWidgetFactory.kt +94 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/ForgeToolWindowFactory.kt +27 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/ForgeTreeView.kt +176 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/Helpers.kt +48 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/PipelinesView.kt +226 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/TerminalsView.kt +309 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/TreeNodeData.kt +33 -0
- package/intellij-plugin/src/main/kotlin/com/aion0/forge/ui/toolwindow/WorkspacesView.kt +166 -0
- package/intellij-plugin/src/main/resources/META-INF/plugin.xml +88 -0
- package/intellij-plugin/src/main/resources/icons/forge.svg +3 -0
- package/lib/agents/index.ts +1 -1
- package/lib/help-docs/00-overview.md +1 -0
- package/lib/help-docs/13-ide-plugins.md +90 -0
- package/lib/help-docs/CLAUDE.md +3 -0
- package/package.json +1 -1
- package/vscode-extension/.vscodeignore +11 -0
- package/vscode-extension/README.md +48 -0
- package/vscode-extension/media/icon.png +0 -0
- package/vscode-extension/media/icon.svg +3 -0
- package/vscode-extension/package-lock.json +4046 -0
- package/vscode-extension/package.json +514 -0
- package/vscode-extension/publish.sh +49 -0
- package/vscode-extension/src/api/client.ts +217 -0
- package/vscode-extension/src/auth/auth.ts +32 -0
- package/vscode-extension/src/commands/auth.ts +44 -0
- package/vscode-extension/src/commands/connection.ts +113 -0
- package/vscode-extension/src/commands/docs.ts +40 -0
- package/vscode-extension/src/commands/pipeline.ts +103 -0
- package/vscode-extension/src/commands/server.ts +50 -0
- package/vscode-extension/src/commands/smith.ts +112 -0
- package/vscode-extension/src/commands/task.ts +43 -0
- package/vscode-extension/src/commands/terminal.ts +279 -0
- package/vscode-extension/src/commands/workspace.ts +138 -0
- package/vscode-extension/src/connection/manager.ts +80 -0
- package/vscode-extension/src/docs/fs-provider.ts +94 -0
- package/vscode-extension/src/docs/result-provider.ts +33 -0
- package/vscode-extension/src/docs/transport.ts +22 -0
- package/vscode-extension/src/extension.ts +314 -0
- package/vscode-extension/src/statusbar.ts +70 -0
- package/vscode-extension/src/terminal/pseudoterm.ts +123 -0
- package/vscode-extension/src/views/docs.ts +145 -0
- package/vscode-extension/src/views/pipelines.ts +222 -0
- package/vscode-extension/src/views/terminals.ts +91 -0
- package/vscode-extension/src/views/workspaces.ts +243 -0
- package/vscode-extension/tsconfig.json +16 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import * as vscode from 'vscode';
|
|
2
|
+
import { ForgeClient } from '../api/client';
|
|
3
|
+
|
|
4
|
+
export class WorkspacesProvider implements vscode.TreeDataProvider<WsItem> {
|
|
5
|
+
private _onDidChange = new vscode.EventEmitter<WsItem | undefined | void>();
|
|
6
|
+
onDidChangeTreeData = this._onDidChange.event;
|
|
7
|
+
|
|
8
|
+
constructor(private client: ForgeClient) {}
|
|
9
|
+
|
|
10
|
+
refresh(): void {
|
|
11
|
+
this._onDidChange.fire();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getTreeItem(el: WsItem): vscode.TreeItem {
|
|
15
|
+
return el;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async getChildren(parent?: WsItem): Promise<WsItem[]> {
|
|
19
|
+
if (!parent) {
|
|
20
|
+
const r = await this.client.listWorkspaces();
|
|
21
|
+
if (r.status === 401 || r.status === 403) return [loginPrompt()];
|
|
22
|
+
if (!r.ok || !Array.isArray(r.data)) return [errorItem(r.error || 'Not connected')];
|
|
23
|
+
if (r.data.length === 0) return [hintItem('No workspaces yet')];
|
|
24
|
+
// Sort by project name so the list order is stable across refreshes
|
|
25
|
+
// (the API's underlying readdir is filesystem-dependent).
|
|
26
|
+
const sorted = [...r.data].sort((a: any, b: any) =>
|
|
27
|
+
(a.projectName || a.id).localeCompare(b.projectName || b.id),
|
|
28
|
+
);
|
|
29
|
+
// Pull daemonActive in parallel for each workspace so we can color the icon.
|
|
30
|
+
const items = await Promise.all(sorted.map(async (ws: any) => {
|
|
31
|
+
const detail = await this.client.getWorkspaceAgents(ws.id);
|
|
32
|
+
const daemonActive = !!detail.data?.daemonActive;
|
|
33
|
+
const ctx = daemonActive ? 'forge.workspace.active' : 'forge.workspace.inactive';
|
|
34
|
+
const item = new WsItem(
|
|
35
|
+
ws.projectName || ws.id,
|
|
36
|
+
vscode.TreeItemCollapsibleState.Collapsed,
|
|
37
|
+
ctx,
|
|
38
|
+
{ workspaceId: ws.id, projectPath: ws.projectPath, daemonActive, view: 'projects' },
|
|
39
|
+
`${ws.projectPath || ''}\n${ws.agentCount} smith(s)\nDaemon: ${daemonActive ? 'active' : 'inactive'}`,
|
|
40
|
+
);
|
|
41
|
+
item.iconPath = new vscode.ThemeIcon(
|
|
42
|
+
daemonActive ? 'circle-large-filled' : 'circle-large-outline',
|
|
43
|
+
new vscode.ThemeColor(daemonActive ? 'charts.green' : 'charts.gray'),
|
|
44
|
+
);
|
|
45
|
+
return item;
|
|
46
|
+
}));
|
|
47
|
+
return items;
|
|
48
|
+
}
|
|
49
|
+
// Workspace expanded → show ROOT smiths (no dependsOn); each smith's downstream
|
|
50
|
+
// chain is shown by expanding the smith itself. This visualizes the DAG using
|
|
51
|
+
// tree nesting (no arrows possible in TreeView, but the chain is faithful).
|
|
52
|
+
if (parent.contextValue?.startsWith('forge.workspace') && parent.meta?.workspaceId) {
|
|
53
|
+
const r = await this.client.getWorkspaceAgents(parent.meta.workspaceId);
|
|
54
|
+
if (!r.ok || !r.data) return [errorItem(r.error || 'Failed to load')];
|
|
55
|
+
const agents: any[] = r.data.agents || [];
|
|
56
|
+
const states: Record<string, any> = r.data.states || {};
|
|
57
|
+
const roots = agents.filter(a => !a.dependsOn || a.dependsOn.length === 0);
|
|
58
|
+
return roots.map(a => buildSmithItem(a, states[a.id] || {}, parent.meta.workspaceId, agents));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Smith expanded → DAG downstream + Inbox + Log virtual sections.
|
|
62
|
+
// Match real smith status nodes only — `forge.smith.<status>[.paused]`.
|
|
63
|
+
// Inbox / Log section nodes use unrelated prefixes so they don't match.
|
|
64
|
+
if (parent.contextValue?.startsWith('forge.smith.') && parent.meta?.workspaceId && parent.meta?.agentId) {
|
|
65
|
+
const r = await this.client.getWorkspaceAgents(parent.meta.workspaceId);
|
|
66
|
+
if (!r.ok || !r.data) return [];
|
|
67
|
+
const agents: any[] = r.data.agents || [];
|
|
68
|
+
const states: Record<string, any> = r.data.states || {};
|
|
69
|
+
const downstream = agents.filter(a => (a.dependsOn || []).includes(parent.meta.agentId));
|
|
70
|
+
const out: WsItem[] = downstream.map(a => buildSmithItem(a, states[a.id] || {}, parent.meta.workspaceId, agents));
|
|
71
|
+
// Always append Inbox + Log section nodes.
|
|
72
|
+
const inboxNode = new WsItem('Inbox', vscode.TreeItemCollapsibleState.Collapsed, 'forge.inboxSection',
|
|
73
|
+
{ workspaceId: parent.meta.workspaceId, agentId: parent.meta.agentId, label: parent.meta.label }, 'Recent bus messages addressed to this smith');
|
|
74
|
+
inboxNode.iconPath = new vscode.ThemeIcon('inbox');
|
|
75
|
+
out.push(inboxNode);
|
|
76
|
+
const logNode = new WsItem('Activity Log', vscode.TreeItemCollapsibleState.Collapsed, 'forge.logSection',
|
|
77
|
+
{ workspaceId: parent.meta.workspaceId, agentId: parent.meta.agentId, label: parent.meta.label }, 'Latest activity log entries');
|
|
78
|
+
logNode.iconPath = new vscode.ThemeIcon('output');
|
|
79
|
+
out.push(logNode);
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Inbox section expanded → list recent messages.
|
|
84
|
+
if (parent.contextValue === 'forge.inboxSection') {
|
|
85
|
+
const r = await this.client.smithAction(parent.meta.workspaceId, 'inbox', parent.meta.agentId);
|
|
86
|
+
if (!r.ok) return [hintItem(`⚠ ${r.error || 'failed to load'}`)];
|
|
87
|
+
const msgs: any[] = r.data?.messages || [];
|
|
88
|
+
if (msgs.length === 0) return [hintItem('No messages')];
|
|
89
|
+
return msgs.slice().reverse().map((m: any) => {
|
|
90
|
+
const item = new WsItem(
|
|
91
|
+
`${msgIcon(m.status)} ${m.from} → ${m.action}`,
|
|
92
|
+
vscode.TreeItemCollapsibleState.None,
|
|
93
|
+
'forge.inboxItem',
|
|
94
|
+
{ messageId: m.id },
|
|
95
|
+
`${m.from} (${m.time})\nstatus: ${m.status}\naction: ${m.action}\n${m.content || ''}`.slice(0, 500),
|
|
96
|
+
);
|
|
97
|
+
item.description = `${m.status} · ${m.time}`;
|
|
98
|
+
return item;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Activity log section expanded → list latest log entries (newest first).
|
|
103
|
+
if (parent.contextValue === 'forge.logSection') {
|
|
104
|
+
const r = await this.client.smithAction(parent.meta.workspaceId, 'logs', parent.meta.agentId);
|
|
105
|
+
if (!r.ok) return [hintItem(`⚠ ${r.error || 'failed to load'}`)];
|
|
106
|
+
const logs: any[] = r.data?.logs || [];
|
|
107
|
+
if (logs.length === 0) return [hintItem('No log entries')];
|
|
108
|
+
return logs.slice(-50).reverse().map((entry: any) => {
|
|
109
|
+
const time = entry.timestamp ? new Date(entry.timestamp).toLocaleTimeString() : '';
|
|
110
|
+
const subtype = entry.subtype || entry.type || 'log';
|
|
111
|
+
const content = (entry.content || '').toString();
|
|
112
|
+
const oneLine = content.replace(/\s+/g, ' ').trim();
|
|
113
|
+
const item = new WsItem(
|
|
114
|
+
`${logIcon(subtype)} ${oneLine.slice(0, 80) || subtype}`,
|
|
115
|
+
vscode.TreeItemCollapsibleState.None,
|
|
116
|
+
'forge.logItem',
|
|
117
|
+
{},
|
|
118
|
+
`[${subtype}] ${time}\n\n${content}`.slice(0, 2000),
|
|
119
|
+
);
|
|
120
|
+
item.description = time;
|
|
121
|
+
return item;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Render one smith item. `allAgents` is the full agent list (used to decide
|
|
130
|
+
* whether to mark this node expandable, and to build the upstream summary). */
|
|
131
|
+
function buildSmithItem(a: any, s: any, workspaceId: string, allAgents: any[]): WsItem {
|
|
132
|
+
const smith = s.smithStatus || 'down';
|
|
133
|
+
const task = s.taskStatus || 'idle';
|
|
134
|
+
const paused = s.paused ? ' ⏸' : '';
|
|
135
|
+
|
|
136
|
+
// Always expandable — children are downstream smiths (DAG) + Inbox + Activity Log.
|
|
137
|
+
const collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
|
|
138
|
+
|
|
139
|
+
// Description: show upstream count "← N" so the user can tell at a glance
|
|
140
|
+
// that this smith depends on others (without expanding the parent path).
|
|
141
|
+
const upstreamIds: string[] = a.dependsOn || [];
|
|
142
|
+
const description = upstreamIds.length > 0
|
|
143
|
+
? `← ${upstreamIds.length}`
|
|
144
|
+
: undefined;
|
|
145
|
+
|
|
146
|
+
// Tooltip with full upstream/downstream list (helps explain multi-parent dups).
|
|
147
|
+
const upLabels = upstreamIds.map(id => labelFor(id, allAgents)).filter(Boolean).join(', ');
|
|
148
|
+
const downLabels = allAgents
|
|
149
|
+
.filter(x => (x.dependsOn || []).includes(a.id))
|
|
150
|
+
.map(x => x.label)
|
|
151
|
+
.join(', ');
|
|
152
|
+
const tooltipParts = [
|
|
153
|
+
`${a.label} (${a.id})`,
|
|
154
|
+
`smith=${smith} task=${task}${s.paused ? ' (paused)' : ''}`,
|
|
155
|
+
];
|
|
156
|
+
if (upLabels) tooltipParts.push(`Depends on: ${upLabels}`);
|
|
157
|
+
if (downLabels) tooltipParts.push(`Triggers: ${downLabels}`);
|
|
158
|
+
|
|
159
|
+
const ctx = `forge.smith.${task}${s.paused ? '.paused' : ''}`;
|
|
160
|
+
const arg = { workspaceId, agentId: a.id, label: a.label, view: 'projects' };
|
|
161
|
+
|
|
162
|
+
const item = new WsItem(
|
|
163
|
+
`${a.icon || '🤖'} ${a.label}${paused}`,
|
|
164
|
+
collapsibleState,
|
|
165
|
+
ctx,
|
|
166
|
+
arg,
|
|
167
|
+
tooltipParts.join('\n'),
|
|
168
|
+
);
|
|
169
|
+
item.description = description;
|
|
170
|
+
item.iconPath = smithIcon(smith, task, !!s.paused);
|
|
171
|
+
item.command = {
|
|
172
|
+
command: 'forge.smithOpenTerminal',
|
|
173
|
+
title: 'Open Smith Terminal',
|
|
174
|
+
arguments: [arg],
|
|
175
|
+
};
|
|
176
|
+
return item;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function labelFor(id: string, allAgents: any[]): string {
|
|
180
|
+
return allAgents.find(a => a.id === id)?.label || id;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
class WsItem extends vscode.TreeItem {
|
|
184
|
+
constructor(
|
|
185
|
+
label: string,
|
|
186
|
+
collapsible: vscode.TreeItemCollapsibleState,
|
|
187
|
+
contextValue: string,
|
|
188
|
+
public meta: any = {},
|
|
189
|
+
tooltip?: string,
|
|
190
|
+
) {
|
|
191
|
+
super(label, collapsible);
|
|
192
|
+
this.contextValue = contextValue;
|
|
193
|
+
if (tooltip) this.tooltip = tooltip;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function errorItem(err: string): WsItem {
|
|
198
|
+
const item = new WsItem(`⚠ ${err}`, vscode.TreeItemCollapsibleState.None, 'forge.error');
|
|
199
|
+
return item;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function hintItem(msg: string): WsItem {
|
|
203
|
+
return new WsItem(msg, vscode.TreeItemCollapsibleState.None, 'forge.hint');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function loginPrompt(): WsItem {
|
|
207
|
+
const item = new WsItem('🔑 Click to login', vscode.TreeItemCollapsibleState.None, 'forge.login');
|
|
208
|
+
item.command = { command: 'forge.login', title: 'Login' };
|
|
209
|
+
return item;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function msgIcon(status: string): string {
|
|
213
|
+
switch (status) {
|
|
214
|
+
case 'pending': return '⌛';
|
|
215
|
+
case 'pending_approval': return '🔒';
|
|
216
|
+
case 'running': return '▶';
|
|
217
|
+
case 'done': return '✓';
|
|
218
|
+
case 'failed': return '✕';
|
|
219
|
+
default: return '·';
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function logIcon(subtype: string): string {
|
|
224
|
+
switch (subtype) {
|
|
225
|
+
case 'error': return '✕';
|
|
226
|
+
case 'warning': return '⚠';
|
|
227
|
+
case 'bus_message': return '✉';
|
|
228
|
+
case 'watch_detected':return '👁';
|
|
229
|
+
case 'hook_done': return '✓';
|
|
230
|
+
case 'system': return 'ℹ';
|
|
231
|
+
default: return '·';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function smithIcon(smith: string, task: string, paused: boolean): vscode.ThemeIcon {
|
|
236
|
+
if (paused) return new vscode.ThemeIcon('debug-pause', new vscode.ThemeColor('charts.orange'));
|
|
237
|
+
if (smith === 'down') return new vscode.ThemeIcon('circle-slash', new vscode.ThemeColor('charts.gray'));
|
|
238
|
+
if (smith === 'starting') return new vscode.ThemeIcon('loading~spin', new vscode.ThemeColor('charts.orange'));
|
|
239
|
+
if (task === 'running') return new vscode.ThemeIcon('debug-start', new vscode.ThemeColor('charts.green'));
|
|
240
|
+
if (task === 'failed') return new vscode.ThemeIcon('error', new vscode.ThemeColor('charts.red'));
|
|
241
|
+
if (task === 'done') return new vscode.ThemeIcon('check', new vscode.ThemeColor('charts.green'));
|
|
242
|
+
return new vscode.ThemeIcon('circle-outline');
|
|
243
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"outDir": "out",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "out", ".vscode-test"]
|
|
16
|
+
}
|