Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7583f28

Browse files
committed
Add agent metadata statusbar to monitor resource usage
1 parente0adfb8 commit7583f28

File tree

3 files changed

+145
-69
lines changed

3 files changed

+145
-69
lines changed

‎src/agentMetadataHelper.ts‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import{Api}from"coder/site/src/api/api";
2+
import{WorkspaceAgent}from"coder/site/src/api/typesGenerated";
3+
import{EventSource}from"eventsource";
4+
import*asvscodefrom"vscode";
5+
import{createStreamingFetchAdapter}from"./api";
6+
import{
7+
AgentMetadataEvent,
8+
AgentMetadataEventSchemaArray,
9+
errToStr,
10+
}from"./api-helper";
11+
12+
exporttypeAgentMetadataWatcher={
13+
onChange:vscode.EventEmitter<null>["event"];
14+
dispose:()=>void;
15+
metadata?:AgentMetadataEvent[];
16+
error?:unknown;
17+
};
18+
19+
/**
20+
* Opens an SSE connection to watch metadata for a given workspace agent.
21+
* Emits onChange when metadata updates or an error occurs.
22+
*/
23+
exportfunctioncreateAgentMetadataWatcher(
24+
agentId:WorkspaceAgent["id"],
25+
restClient:Api,
26+
):AgentMetadataWatcher{
27+
// TODO: Is there a better way to grab the url and token?
28+
consturl=restClient.getAxiosInstance().defaults.baseURL;
29+
constmetadataUrl=newURL(
30+
`${url}/api/v2/workspaceagents/${agentId}/watch-metadata`,
31+
);
32+
consteventSource=newEventSource(metadataUrl.toString(),{
33+
fetch:createStreamingFetchAdapter(restClient.getAxiosInstance()),
34+
});
35+
36+
letdisposed=false;
37+
constonChange=newvscode.EventEmitter<null>();
38+
constwatcher:AgentMetadataWatcher={
39+
onChange:onChange.event,
40+
dispose:()=>{
41+
if(!disposed){
42+
eventSource.close();
43+
disposed=true;
44+
}
45+
},
46+
};
47+
48+
eventSource.addEventListener("data",(event)=>{
49+
try{
50+
constdataEvent=JSON.parse(event.data);
51+
constmetadata=AgentMetadataEventSchemaArray.parse(dataEvent);
52+
53+
// Overwrite metadata if it changed.
54+
if(JSON.stringify(watcher.metadata)!==JSON.stringify(metadata)){
55+
watcher.metadata=metadata;
56+
onChange.fire(null);
57+
}
58+
}catch(error){
59+
watcher.error=error;
60+
onChange.fire(null);
61+
}
62+
});
63+
64+
returnwatcher;
65+
}
66+
67+
exportfunctionformatMetadataError(error:unknown):string{
68+
return"Failed to query metadata: "+errToStr(error,"no error provided");
69+
}
70+
71+
exportfunctionformatEventLabel(metadataEvent:AgentMetadataEvent):string{
72+
returngetEventName(metadataEvent)+": "+getEventValue(metadataEvent);
73+
}
74+
75+
exportfunctiongetEventName(metadataEvent:AgentMetadataEvent):string{
76+
returnmetadataEvent.description.display_name.trim();
77+
}
78+
79+
exportfunctiongetEventValue(metadataEvent:AgentMetadataEvent):string{
80+
returnmetadataEvent.result.value.replace(/\n/g,"").trim();
81+
}

‎src/remote.ts‎

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import{isAxiosError}from"axios";
22
import{Api}from"coder/site/src/api/api";
3-
import{Workspace}from"coder/site/src/api/typesGenerated";
3+
import{Workspace,WorkspaceAgent}from"coder/site/src/api/typesGenerated";
44
importfindfrom"find-process";
55
import*asfsfrom"fs/promises";
66
import*asjsoncfrom"jsonc-parser";
@@ -9,6 +9,12 @@ import * as path from "path";
99
importprettyBytesfrom"pretty-bytes";
1010
import*assemverfrom"semver";
1111
import*asvscodefrom"vscode";
12+
import{
13+
createAgentMetadataWatcher,
14+
getEventValue,
15+
formatEventLabel,
16+
formatMetadataError,
17+
}from"./agentMetadataHelper";
1218
import{
1319
createHttpAgent,
1420
makeCoderSdk,
@@ -633,6 +639,8 @@ export class Remote {
633639
}),
634640
);
635641

642+
this.createAgentMetadataStatusBar(agent,workspaceRestClient,disposables);
643+
636644
this.storage.writeToCoderOutputChannel("Remote setup complete");
637645

638646
// Returning the URL and token allows the plugin to authenticate its own
@@ -974,6 +982,50 @@ export class Remote {
974982
returnloop();
975983
}
976984

985+
/**
986+
* Creates and manages a status bar item that displays metadata information for a given workspace agent.
987+
* The status bar item updates dynamically based on changes to the agent's metadata,
988+
* and hides itself if no metadata is available or an error occurs.
989+
*/
990+
privatecreateAgentMetadataStatusBar(
991+
agent:WorkspaceAgent,
992+
restClient:Api,
993+
disposables:vscode.Disposable[],
994+
):void{
995+
conststatusBarItem=vscode.window.createStatusBarItem(
996+
"agentMetadata",
997+
vscode.StatusBarAlignment.Left,
998+
);
999+
disposables.push(statusBarItem);
1000+
1001+
constagentWatcher=createAgentMetadataWatcher(agent.id,restClient);
1002+
disposables.push(agentWatcher);
1003+
1004+
agentWatcher.onChange(
1005+
()=>{
1006+
if(agentWatcher.error){
1007+
this.storage.writeToCoderOutputChannel(
1008+
formatMetadataError(agentWatcher.error),
1009+
);
1010+
statusBarItem.hide();
1011+
return;
1012+
}
1013+
1014+
if(agentWatcher.metadata&&agentWatcher.metadata.length>0){
1015+
statusBarItem.text=getEventValue(agentWatcher.metadata[0]);
1016+
statusBarItem.tooltip=agentWatcher.metadata
1017+
.map((metadata)=>formatEventLabel(metadata))
1018+
.join("\n");
1019+
statusBarItem.show();
1020+
}else{
1021+
statusBarItem.hide();
1022+
}
1023+
},
1024+
undefined,
1025+
disposables,
1026+
);
1027+
}
1028+
9771029
// closeRemote ends the current remote session.
9781030
publicasynccloseRemote(){
9791031
awaitvscode.commands.executeCommand("workbench.action.remote.close");

‎src/workspacesProvider.ts‎

Lines changed: 11 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@ import {
44
WorkspaceAgent,
55
WorkspaceApp,
66
}from"coder/site/src/api/typesGenerated";
7-
import{EventSource}from"eventsource";
87
import*aspathfrom"path";
98
import*asvscodefrom"vscode";
10-
import{createStreamingFetchAdapter}from"./api";
9+
import{
10+
AgentMetadataWatcher,
11+
createAgentMetadataWatcher,
12+
formatEventLabel,
13+
formatMetadataError,
14+
}from"./agentMetadataHelper";
1115
import{
1216
AgentMetadataEvent,
13-
AgentMetadataEventSchemaArray,
1417
extractAllAgents,
1518
extractAgents,
16-
errToStr,
1719
}from"./api-helper";
1820
import{Storage}from"./storage";
1921

@@ -22,13 +24,6 @@ export enum WorkspaceQuery {
2224
All="",
2325
}
2426

25-
typeAgentWatcher={
26-
onChange:vscode.EventEmitter<null>["event"];
27-
dispose:()=>void;
28-
metadata?:AgentMetadataEvent[];
29-
error?:unknown;
30-
};
31-
3227
/**
3328
* Polls workspaces using the provided REST client and renders them in a tree.
3429
*
@@ -42,7 +37,8 @@ export class WorkspaceProvider
4237
{
4338
// Undefined if we have never fetched workspaces before.
4439
privateworkspaces:WorkspaceTreeItem[]|undefined;
45-
privateagentWatchers:Record<WorkspaceAgent["id"],AgentWatcher>={};
40+
privateagentWatchers:Record<WorkspaceAgent["id"],AgentMetadataWatcher>=
41+
{};
4642
privatetimeout:NodeJS.Timeout|undefined;
4743
privatefetching=false;
4844
privatevisible=false;
@@ -139,7 +135,7 @@ export class WorkspaceProvider
139135
returnthis.agentWatchers[agent.id];
140136
}
141137
// Otherwise create a new watcher.
142-
constwatcher=monitorMetadata(agent.id,restClient);
138+
constwatcher=createAgentMetadataWatcher(agent.id,restClient);
143139
watcher.onChange(()=>this.refresh());
144140
this.agentWatchers[agent.id]=watcher;
145141
returnwatcher;
@@ -313,53 +309,6 @@ export class WorkspaceProvider
313309
}
314310
}
315311

316-
// monitorMetadata opens an SSE endpoint to monitor metadata on the specified
317-
// agent and registers a watcher that can be disposed to stop the watch and
318-
// emits an event when the metadata changes.
319-
functionmonitorMetadata(
320-
agentId:WorkspaceAgent["id"],
321-
restClient:Api,
322-
):AgentWatcher{
323-
// TODO: Is there a better way to grab the url and token?
324-
consturl=restClient.getAxiosInstance().defaults.baseURL;
325-
constmetadataUrl=newURL(
326-
`${url}/api/v2/workspaceagents/${agentId}/watch-metadata`,
327-
);
328-
consteventSource=newEventSource(metadataUrl.toString(),{
329-
fetch:createStreamingFetchAdapter(restClient.getAxiosInstance()),
330-
});
331-
332-
letdisposed=false;
333-
constonChange=newvscode.EventEmitter<null>();
334-
constwatcher:AgentWatcher={
335-
onChange:onChange.event,
336-
dispose:()=>{
337-
if(!disposed){
338-
eventSource.close();
339-
disposed=true;
340-
}
341-
},
342-
};
343-
344-
eventSource.addEventListener("data",(event)=>{
345-
try{
346-
constdataEvent=JSON.parse(event.data);
347-
constmetadata=AgentMetadataEventSchemaArray.parse(dataEvent);
348-
349-
// Overwrite metadata if it changed.
350-
if(JSON.stringify(watcher.metadata)!==JSON.stringify(metadata)){
351-
watcher.metadata=metadata;
352-
onChange.fire(null);
353-
}
354-
}catch(error){
355-
watcher.error=error;
356-
onChange.fire(null);
357-
}
358-
});
359-
360-
returnwatcher;
361-
}
362-
363312
/**
364313
* A tree item that represents a collapsible section with child items
365314
*/
@@ -375,20 +324,14 @@ class SectionTreeItem extends vscode.TreeItem {
375324

376325
classErrorTreeItemextendsvscode.TreeItem{
377326
constructor(error:unknown){
378-
super(
379-
"Failed to query metadata: "+errToStr(error,"no error provided"),
380-
vscode.TreeItemCollapsibleState.None,
381-
);
327+
super(formatMetadataError(error),vscode.TreeItemCollapsibleState.None);
382328
this.contextValue="coderAgentMetadata";
383329
}
384330
}
385331

386332
classAgentMetadataTreeItemextendsvscode.TreeItem{
387333
constructor(metadataEvent:AgentMetadataEvent){
388-
constlabel=
389-
metadataEvent.description.display_name.trim()+
390-
": "+
391-
metadataEvent.result.value.replace(/\n/g,"").trim();
334+
constlabel=formatEventLabel(metadataEvent);
392335

393336
super(label,vscode.TreeItemCollapsibleState.None);
394337
constcollected_at=newDate(

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp