11import { field , logger } from "@coder/logger"
22import * as cp from "child_process"
3+ import * as fs from "fs-extra"
34import * as http from "http"
45import * as net from "net"
6+ import * as path from "path"
7+ import * as url from "url"
58import * as WebSocket from "ws"
69import {
710 Application ,
811 ApplicationsResponse ,
912 ClientMessage ,
1013 RecentResponse ,
14+ RunningResponse ,
1115 ServerMessage ,
1216 SessionError ,
1317 SessionResponse ,
@@ -22,14 +26,24 @@ interface ServerSession {
2226 readonly app : Application
2327}
2428
29+ interface VsRecents {
30+ [ key : string ] : ( string | object ) [ ]
31+ }
32+
33+ type VsSettings = [ string , string ] [ ]
34+
2535/**
2636 * API HTTP provider.
2737 */
2838export class ApiHttpProvider extends HttpProvider {
2939 private readonly ws = new WebSocket . Server ( { noServer : true } )
3040 private readonly sessions = new Map < string , ServerSession > ( )
3141
32- public constructor ( options : HttpProviderOptions , private readonly server : HttpServer ) {
42+ public constructor (
43+ options : HttpProviderOptions ,
44+ private readonly server : HttpServer ,
45+ private readonly dataDir ?: string ,
46+ ) {
3347 super ( options )
3448 }
3549
@@ -60,6 +74,11 @@ export class ApiHttpProvider extends HttpProvider {
6074 return {
6175 content : await this . recent ( ) ,
6276 } as HttpResponse < RecentResponse >
77+ case ApiEndpoint . running :
78+ this . ensureMethod ( request )
79+ return {
80+ content : await this . running ( ) ,
81+ } as HttpResponse < RunningResponse >
6382 }
6483 return undefined
6584 }
@@ -280,12 +299,58 @@ export class ApiHttpProvider extends HttpProvider {
280299 }
281300
282301 /**
283- * Return recent sessions .
302+ * Return VS Code's recent paths .
284303 */
285304 public async recent ( ) : Promise < RecentResponse > {
305+ try {
306+ if ( ! this . dataDir ) {
307+ throw new Error ( "data directory is not set" )
308+ }
309+
310+ const state : VsSettings = JSON . parse ( await fs . readFile ( path . join ( this . dataDir , "User/state/global.json" ) , "utf8" ) )
311+ const setting = Array . isArray ( state ) && state . find ( ( item ) => item [ 0 ] === "recently.opened" )
312+ if ( ! setting ) {
313+ throw new Error ( "settings appear malformed" )
314+ }
315+
316+ const paths : { [ key : string ] : Promise < string > } = { }
317+ Object . values ( JSON . parse ( setting [ 1 ] ) as VsRecents ) . forEach ( ( recents ) => {
318+ recents
319+ . filter ( ( recent ) => typeof recent === "string" )
320+ . forEach ( ( recent ) => {
321+ try {
322+ const pathname = url . parse ( recent as string ) . pathname
323+ if ( pathname && ! paths [ pathname ] ) {
324+ paths [ pathname ] = new Promise < string > ( ( resolve ) => {
325+ fs . stat ( pathname )
326+ . then ( ( ) => resolve ( pathname ) )
327+ . catch ( ( ) => resolve ( ) )
328+ } )
329+ }
330+ } catch ( error ) {
331+ logger . debug ( "invalid path" , field ( "path" , recent ) )
332+ }
333+ } )
334+ } )
335+
336+ return {
337+ paths : await Promise . all ( Object . values ( paths ) ) ,
338+ }
339+ } catch ( error ) {
340+ if ( error . code !== "ENOENT" ) {
341+ throw error
342+ }
343+ }
344+
345+ return { paths : [ ] }
346+ }
347+
348+ /**
349+ * Return running sessions.
350+ */
351+ public async running ( ) : Promise < RunningResponse > {
286352 return {
287- recent : [ ] , // TODO
288- running : Array . from ( this . sessions ) . map ( ( [ sessionId , session ] ) => ( {
353+ applications : Array . from ( this . sessions ) . map ( ( [ sessionId , session ] ) => ( {
289354 ...session . app ,
290355 sessionId,
291356 } ) ) ,
0 commit comments