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

Commit0e76e62

Browse files
authored
feat: show agent metadata (#92)
1 parent4c37680 commit0e76e62

File tree

7 files changed

+170
-65
lines changed

7 files changed

+170
-65
lines changed

‎package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@
226226
"tar-fs":"^2.1.1",
227227
"which":"^2.0.2",
228228
"ws":"^8.11.0",
229-
"yaml":"^1.10.0"
229+
"yaml":"^1.10.0",
230+
"zod":"^3.21.4"
230231
}
231232
}

‎src/api-helper.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import{Workspace,WorkspaceAgent}from"coder/site/src/api/typesGenerated"
2+
import{z}from"zod"
23

34
exportfunctionextractAgents(workspace:Workspace):WorkspaceAgent[]{
45
constagents=workspace.latest_build.resources.reduce((acc,resource)=>{
@@ -7,3 +8,23 @@ export function extractAgents(workspace: Workspace): WorkspaceAgent[] {
78

89
returnagents
910
}
11+
12+
exportconstAgentMetadataEventSchema=z.object({
13+
result:z.object({
14+
collected_at:z.string(),
15+
age:z.number(),
16+
value:z.string(),
17+
error:z.string(),
18+
}),
19+
description:z.object({
20+
display_name:z.string(),
21+
key:z.string(),
22+
script:z.string(),
23+
interval:z.number(),
24+
timeout:z.number(),
25+
}),
26+
})
27+
28+
exportconstAgentMetadataEventSchemaArray=z.array(AgentMetadataEventSchema)
29+
30+
exporttypeAgentMetadataEvent=z.infer<typeofAgentMetadataEventSchema>

‎src/commands.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as vscode from "vscode"
55
import{extractAgents}from"./api-helper"
66
import{Remote}from"./remote"
77
import{Storage}from"./storage"
8-
import{WorkspaceTreeItem}from"./workspacesProvider"
8+
import{OpenableTreeItem}from"./workspacesProvider"
99

1010
exportclassCommands{
1111
publicconstructor(privatereadonlyvscodeProposed:typeofvscode,privatereadonlystorage:Storage){}
@@ -118,7 +118,7 @@ export class Commands {
118118
awaitvscode.commands.executeCommand("vscode.open",uri)
119119
}
120120

121-
publicasyncnavigateToWorkspace(workspace:WorkspaceTreeItem){
121+
publicasyncnavigateToWorkspace(workspace:OpenableTreeItem){
122122
if(workspace){
123123
consturi=this.storage.getURL()+`/@${workspace.workspaceOwner}/${workspace.workspaceName}`
124124
awaitvscode.commands.executeCommand("vscode.open",uri)
@@ -130,7 +130,7 @@ export class Commands {
130130
}
131131
}
132132

133-
publicasyncnavigateToWorkspaceSettings(workspace:WorkspaceTreeItem){
133+
publicasyncnavigateToWorkspaceSettings(workspace:OpenableTreeItem){
134134
if(workspace){
135135
consturi=this.storage.getURL()+`/@${workspace.workspaceOwner}/${workspace.workspaceName}/settings`
136136
awaitvscode.commands.executeCommand("vscode.open",uri)
@@ -143,7 +143,7 @@ export class Commands {
143143
}
144144
}
145145

146-
publicasyncopenFromSidebar(treeItem:WorkspaceTreeItem){
146+
publicasyncopenFromSidebar(treeItem:OpenableTreeItem){
147147
if(treeItem){
148148
awaitopenWorkspace(
149149
treeItem.workspaceOwner,

‎src/extension.ts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
"use strict"
2-
32
import{getAuthenticatedUser}from"coder/site/src/api/api"
43
import*asmodulefrom"module"
54
import*asvscodefrom"vscode"
@@ -13,8 +12,8 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
1312
conststorage=newStorage(output,ctx.globalState,ctx.secrets,ctx.globalStorageUri,ctx.logUri)
1413
awaitstorage.init()
1514

16-
constmyWorkspacesProvider=newWorkspaceProvider(WorkspaceQuery.Mine)
17-
constallWorkspacesProvider=newWorkspaceProvider(WorkspaceQuery.All)
15+
constmyWorkspacesProvider=newWorkspaceProvider(WorkspaceQuery.Mine,storage)
16+
constallWorkspacesProvider=newWorkspaceProvider(WorkspaceQuery.All,storage)
1817

1918
vscode.window.registerTreeDataProvider("myWorkspaces",myWorkspacesProvider)
2019
vscode.window.registerTreeDataProvider("allWorkspaces",allWorkspacesProvider)

‎src/remote.ts‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,6 @@ export class Remote {
282282
"Coder-Session-Token":awaitthis.storage.getSessionToken(),
283283
},
284284
})
285-
eventSource.addEventListener("open",()=>{
286-
// TODO: Add debug output that we began watching here!
287-
})
288-
eventSource.addEventListener("error",()=>{
289-
// TODO: Add debug output that we got an error here!
290-
})
291285

292286
constworkspaceUpdatedStatus=vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left,999)
293287
disposables.push(workspaceUpdatedStatus)

‎src/workspacesProvider.ts‎

Lines changed: 136 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,178 @@
11
import{getWorkspaces}from"coder/site/src/api/api"
2-
import{WorkspaceAgent}from"coder/site/src/api/typesGenerated"
2+
import{Workspace,WorkspaceAgent}from"coder/site/src/api/typesGenerated"
3+
importEventSourcefrom"eventsource"
34
import*aspathfrom"path"
45
import*asvscodefrom"vscode"
5-
import{extractAgents}from"./api-helper"
6+
import{AgentMetadataEvent,AgentMetadataEventSchemaArray,extractAgents}from"./api-helper"
7+
import{Storage}from"./storage"
68

79
exportenumWorkspaceQuery{
810
Mine="owner:me",
911
All="",
1012
}
1113

12-
exportclassWorkspaceProviderimplementsvscode.TreeDataProvider<WorkspaceTreeItem>{
13-
constructor(privatereadonlygetWorkspacesQuery:WorkspaceQuery){}
14+
exportclassWorkspaceProviderimplementsvscode.TreeDataProvider<vscode.TreeItem>{
15+
privateworkspaces:WorkspaceTreeItem[]=[]
16+
privateagentMetadata:Record<WorkspaceAgent["id"],AgentMetadataEvent[]>={}
1417

15-
private_onDidChangeTreeData:vscode.EventEmitter<WorkspaceTreeItem|undefined|null|void>=
16-
newvscode.EventEmitter<WorkspaceTreeItem|undefined|null|void>()
17-
readonlyonDidChangeTreeData:vscode.Event<WorkspaceTreeItem|undefined|null|void>=
18+
constructor(privatereadonlygetWorkspacesQuery:WorkspaceQuery,privatereadonlystorage:Storage){
19+
getWorkspaces({q:this.getWorkspacesQuery})
20+
.then((workspaces)=>{
21+
constworkspacesTreeItem:WorkspaceTreeItem[]=[]
22+
workspaces.workspaces.forEach((workspace)=>{
23+
constshowMetadata=this.getWorkspacesQuery===WorkspaceQuery.Mine
24+
if(showMetadata){
25+
constagents=extractAgents(workspace)
26+
agents.forEach((agent)=>this.monitorMetadata(agent.id))// monitor metadata for all agents
27+
}
28+
consttreeItem=newWorkspaceTreeItem(
29+
workspace,
30+
this.getWorkspacesQuery===WorkspaceQuery.All,
31+
showMetadata,
32+
)
33+
workspacesTreeItem.push(treeItem)
34+
})
35+
returnworkspacesTreeItem
36+
})
37+
.then((workspaces)=>{
38+
this.workspaces=workspaces
39+
this.refresh()
40+
})
41+
}
42+
43+
private_onDidChangeTreeData:vscode.EventEmitter<vscode.TreeItem|undefined|null|void>=
44+
newvscode.EventEmitter<vscode.TreeItem|undefined|null|void>()
45+
readonlyonDidChangeTreeData:vscode.Event<vscode.TreeItem|undefined|null|void>=
1846
this._onDidChangeTreeData.event
1947

20-
refresh():void{
21-
this._onDidChangeTreeData.fire()
48+
refresh(item:vscode.TreeItem|undefined|null|void):void{
49+
this._onDidChangeTreeData.fire(item)
2250
}
2351

24-
getTreeItem(element:WorkspaceTreeItem):vscode.TreeItem{
52+
asyncgetTreeItem(element:vscode.TreeItem):Promise<vscode.TreeItem>{
2553
returnelement
2654
}
2755

28-
getChildren(element?:WorkspaceTreeItem):Thenable<WorkspaceTreeItem[]>{
56+
getChildren(element?:vscode.TreeItem):Thenable<vscode.TreeItem[]>{
2957
if(element){
30-
if(element.agents.length>0){
31-
returnPromise.resolve(
32-
element.agents.map((agent)=>{
33-
constlabel=agent.name
34-
constdetail=`Status:${agent.status}`
35-
returnnewWorkspaceTreeItem(label,detail,"","",agent.name,agent.expanded_directory,[],"coderAgent")
36-
}),
37-
)
58+
if(elementinstanceofWorkspaceTreeItem){
59+
constagents=extractAgents(element.workspace)
60+
constagentTreeItems=agents.map((agent)=>newAgentTreeItem(agent,element.watchMetadata))
61+
returnPromise.resolve(agentTreeItems)
62+
}elseif(elementinstanceofAgentTreeItem){
63+
constsavedMetadata=this.agentMetadata[element.agent.id]||[]
64+
returnPromise.resolve(savedMetadata.map((metadata)=>newAgentMetadataTreeItem(metadata)))
3865
}
66+
3967
returnPromise.resolve([])
4068
}
41-
returngetWorkspaces({q:this.getWorkspacesQuery}).then((workspaces)=>{
42-
returnworkspaces.workspaces.map((workspace)=>{
43-
conststatus=
44-
workspace.latest_build.status.substring(0,1).toUpperCase()+workspace.latest_build.status.substring(1)
45-
46-
constlabel=
47-
this.getWorkspacesQuery===WorkspaceQuery.All
48-
?`${workspace.owner_name} /${workspace.name}`
49-
:workspace.name
50-
constdetail=`Template:${workspace.template_display_name||workspace.template_name} • Status:${status}`
51-
constagents=extractAgents(workspace)
52-
returnnewWorkspaceTreeItem(
53-
label,
54-
detail,
55-
workspace.owner_name,
56-
workspace.name,
57-
undefined,
58-
agents[0]?.expanded_directory,
59-
agents,
60-
agents.length>1 ?"coderWorkspaceMultipleAgents" :"coderWorkspaceSingleAgent",
61-
)
62-
})
69+
returnPromise.resolve(this.workspaces)
70+
}
71+
72+
asyncmonitorMetadata(agentId:WorkspaceAgent["id"]):Promise<void>{
73+
constagentMetadataURL=newURL(`${this.storage.getURL()}/api/v2/workspaceagents/${agentId}/watch-metadata`)
74+
constagentMetadataEventSource=newEventSource(agentMetadataURL.toString(),{
75+
headers:{
76+
"Coder-Session-Token":awaitthis.storage.getSessionToken(),
77+
},
78+
})
79+
80+
agentMetadataEventSource.addEventListener("data",(event)=>{
81+
try{
82+
constdataEvent=JSON.parse(event.data)
83+
constagentMetadata=AgentMetadataEventSchemaArray.parse(dataEvent)
84+
85+
if(agentMetadata.length===0){
86+
agentMetadataEventSource.close()
87+
}
88+
89+
constsavedMetadata=this.agentMetadata[agentId]
90+
if(JSON.stringify(savedMetadata)!==JSON.stringify(agentMetadata)){
91+
this.agentMetadata[agentId]=agentMetadata// overwrite existing metadata
92+
this.refresh()
93+
}
94+
}catch(error){
95+
agentMetadataEventSource.close()
96+
}
6397
})
6498
}
6599
}
66100

67101
typeCoderTreeItemType="coderWorkspaceSingleAgent"|"coderWorkspaceMultipleAgents"|"coderAgent"
68102

69-
exportclassWorkspaceTreeItemextendsvscode.TreeItem{
103+
classAgentMetadataTreeItemextendsvscode.TreeItem{
104+
constructor(metadataEvent:AgentMetadataEvent){
105+
constlabel=
106+
metadataEvent.description.display_name.trim()+": "+metadataEvent.result.value.replace(/\n/g,"").trim()
107+
108+
super(label,vscode.TreeItemCollapsibleState.None)
109+
this.tooltip="Collected at "+metadataEvent.result.collected_at
110+
this.contextValue="coderAgentMetadata"
111+
}
112+
}
113+
114+
exportclassOpenableTreeItemextendsvscode.TreeItem{
70115
constructor(
71-
publicreadonlylabel:string,
72-
publicreadonlytooltip:string,
116+
label:string,
117+
tooltip:string,
118+
collapsibleState:vscode.TreeItemCollapsibleState,
119+
73120
publicreadonlyworkspaceOwner:string,
74121
publicreadonlyworkspaceName:string,
75122
publicreadonlyworkspaceAgent:string|undefined,
76123
publicreadonlyworkspaceFolderPath:string|undefined,
77-
publicreadonlyagents:WorkspaceAgent[],
124+
78125
contextValue:CoderTreeItemType,
79126
){
80-
super(
81-
label,
82-
contextValue==="coderWorkspaceMultipleAgents"
83-
?vscode.TreeItemCollapsibleState.Collapsed
84-
:vscode.TreeItemCollapsibleState.None,
85-
)
127+
super(label,collapsibleState)
86128
this.contextValue=contextValue
129+
this.tooltip=tooltip
87130
}
88131

89132
iconPath={
90133
light:path.join(__filename,"..","..","media","logo.svg"),
91134
dark:path.join(__filename,"..","..","media","logo.svg"),
92135
}
93136
}
137+
138+
classAgentTreeItemextendsOpenableTreeItem{
139+
constructor(publicreadonlyagent:WorkspaceAgent,watchMetadata=false){
140+
constlabel=agent.name
141+
constdetail=`Status:${agent.status}`
142+
super(
143+
label,
144+
detail,
145+
watchMetadata ?vscode.TreeItemCollapsibleState.Collapsed :vscode.TreeItemCollapsibleState.None,
146+
"",
147+
"",
148+
agent.name,
149+
agent.expanded_directory,
150+
"coderAgent",
151+
)
152+
}
153+
}
154+
155+
exportclassWorkspaceTreeItemextendsOpenableTreeItem{
156+
constructor(
157+
publicreadonlyworkspace:Workspace,
158+
publicreadonlyshowOwner:boolean,
159+
publicreadonlywatchMetadata=false,
160+
){
161+
conststatus=
162+
workspace.latest_build.status.substring(0,1).toUpperCase()+workspace.latest_build.status.substring(1)
163+
164+
constlabel=showOwner ?`${workspace.owner_name} /${workspace.name}` :workspace.name
165+
constdetail=`Template:${workspace.template_display_name||workspace.template_name} • Status:${status}`
166+
constagents=extractAgents(workspace)
167+
super(
168+
label,
169+
detail,
170+
showOwner ?vscode.TreeItemCollapsibleState.Collapsed :vscode.TreeItemCollapsibleState.Expanded,
171+
workspace.owner_name,
172+
workspace.name,
173+
undefined,
174+
agents[0]?.expanded_directory,
175+
"coderWorkspaceMultipleAgents",
176+
)
177+
}
178+
}

‎yarn.lock‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5761,3 +5761,8 @@ yocto-queue@^1.0.0:
57615761
version "1.0.0"
57625762
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
57635763
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
5764+
5765+
zod@^3.21.4:
5766+
version "3.21.4"
5767+
resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
5768+
integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp