@@ -23,15 +23,18 @@ import type { Server } from 'http';
2323import path from 'node:path' ;
2424import http from 'node:http' ;
2525import { existsSync } from 'fs' ;
26- import { getFreeRandomPort } from '../utils/ports' ;
2726import * as podmanDesktopApi from '@podman-desktop/api' ;
2827import { readFile } from 'fs/promises' ;
2928import type { ModelsManager } from './modelsManager' ;
3029import type { components } from '../../src-generated/openapi' ;
3130import type { ModelInfo } from '@shared/src/models/IModelInfo' ;
31+ import type { ConfigurationRegistry } from '../registries/ConfigurationRegistry' ;
32+ import { getFreeRandomPort } from '../utils/ports' ;
3233
33- const DEFAULT_PORT = 10434 ;
3434const SHOW_API_INFO_COMMAND = 'ai-lab.show-api-info' ;
35+ const SHOW_API_ERROR_COMMAND = 'ai-lab.show-api-error' ;
36+
37+ export const PREFERENCE_RANDOM_PORT = 0 ;
3538
3639type ListModelResponse = components [ 'schemas' ] [ 'ListModelResponse' ] ;
3740
@@ -52,6 +55,7 @@ export class ApiServer implements Disposable {
5255 constructor (
5356 private extensionContext : podmanDesktopApi . ExtensionContext ,
5457 private modelsManager : ModelsManager ,
58+ private configurationRegistry : ConfigurationRegistry ,
5559 ) { }
5660
5761 protected getListener ( ) : Server | undefined {
@@ -72,22 +76,25 @@ export class ApiServer implements Disposable {
7276 app . use ( '/spec' , this . getSpec . bind ( this ) ) ;
7377
7478 const server = http . createServer ( app ) ;
75- let listeningOn = DEFAULT_PORT ;
79+ let listeningOn = this . configurationRegistry . getExtensionConfiguration ( ) . apiPort ;
7680 server . on ( 'listening' , ( ) => {
7781 this . displayApiInfo ( listeningOn ) ;
7882 } ) ;
7983 server . on ( 'error' , ( ) => {
84+ this . displayApiError ( listeningOn ) ;
85+ } ) ;
86+ if ( listeningOn === PREFERENCE_RANDOM_PORT ) {
8087 getFreeRandomPort ( '0.0.0.0' )
8188 . then ( ( randomPort : number ) => {
82- console . warn ( `port ${ DEFAULT_PORT } in use, using ${ randomPort } for API server` ) ;
8389 listeningOn = randomPort ;
84- this . #listener = server . listen ( randomPort ) ;
90+ this . #listener = server . listen ( listeningOn ) ;
8591 } )
8692 . catch ( ( e : unknown ) => {
8793 console . error ( 'unable to get a free port for the api server' , e ) ;
8894 } ) ;
89- } ) ;
90- this . #listener = server . listen ( DEFAULT_PORT ) ;
95+ } else {
96+ this . #listener = server . listen ( listeningOn ) ;
97+ }
9198 }
9299
93100 displayApiInfo ( port : number ) : void {
@@ -111,6 +118,23 @@ export class ApiServer implements Disposable {
111118 apiStatusBarItem . show ( ) ;
112119 }
113120
121+ displayApiError ( port : number ) : void {
122+ const apiStatusBarItem = podmanDesktopApi . window . createStatusBarItem ( ) ;
123+ apiStatusBarItem . text = `AI Lab API listening error` ;
124+ apiStatusBarItem . command = SHOW_API_ERROR_COMMAND ;
125+ this . extensionContext . subscriptions . push (
126+ podmanDesktopApi . commands . registerCommand ( SHOW_API_ERROR_COMMAND , async ( ) => {
127+ const address = `http://localhost:${ port } ` ;
128+ await podmanDesktopApi . window . showErrorMessage (
129+ `AI Lab API failed to listen on\n${ address } \nYou can change the port in the Preferences then restart the extension.` ,
130+ 'OK' ,
131+ ) ;
132+ } ) ,
133+ apiStatusBarItem ,
134+ ) ;
135+ apiStatusBarItem . show ( ) ;
136+ }
137+
114138 private getFile ( filepath : string ) : string {
115139 // when plugin is installed, the file is placed in the plugin directory (~/.local/share/containers/podman-desktop/plugins/<pluginname>/)
116140 const prodFile = path . join ( __dirname , filepath ) ;
0 commit comments