11import { app } from "electron"
2- import {
3- DEFAULT_SERVER_URL_KEY ,
4- INBOUND_ENABLED_KEY ,
5- INBOUND_PASSWORD_KEY ,
6- INBOUND_PORT_KEY ,
7- INBOUND_USERNAME_KEY ,
8- WSL_ENABLED_KEY ,
9- } from "./constants"
2+ import { existsSync , mkdirSync , readFileSync , writeFileSync } from "node:fs"
3+ import os from "node:os"
4+ import path from "node:path"
5+ import { DEFAULT_SERVER_URL_KEY , WSL_ENABLED_KEY } from "./constants"
106import { getUserShell , loadShellEnv } from "./shell-env"
117import { getStore } from "./store"
128
@@ -16,6 +12,7 @@ export type InboundServerConfig = { enabled: boolean; username: string; password
1612export type HealthCheck = { wait : Promise < void > }
1713
1814let runtimeInboundServerConfig : InboundServerConfig | null = null
15+ const emptyInboundServerConfig = { enabled : false , username : "" , password : "" , port : null } satisfies InboundServerConfig
1916
2017export function setRuntimeInboundServerConfig ( config : InboundServerConfig ) {
2118 runtimeInboundServerConfig = config
@@ -44,33 +41,87 @@ export function setWslConfig(config: WslConfig) {
4441 getStore ( ) . set ( WSL_ENABLED_KEY , config . enabled )
4542}
4643
47- export function getInboundServerConfig ( ) : InboundServerConfig {
48- const enabled = getStore ( ) . get ( INBOUND_ENABLED_KEY )
49- const username = getStore ( ) . get ( INBOUND_USERNAME_KEY )
50- const password = getStore ( ) . get ( INBOUND_PASSWORD_KEY )
51- const port = getStore ( ) . get ( INBOUND_PORT_KEY )
44+ function configDir ( ) {
45+ if ( process . env . OPENCODE_CONFIG_DIR ?. trim ( ) ) return process . env . OPENCODE_CONFIG_DIR . trim ( )
46+ return path . join ( process . env . XDG_CONFIG_HOME ?. trim ( ) || path . join ( os . homedir ( ) , ".config" ) , "opencode" )
47+ }
48+
49+ function configFile ( ) {
50+ const dir = configDir ( )
51+ for ( const name of [ "opencode.jsonc" , "opencode.json" , "config.json" ] ) {
52+ const file = path . join ( dir , name )
53+ if ( existsSync ( file ) ) return file
54+ }
55+ return path . join ( dir , "opencode.jsonc" )
56+ }
57+
58+ function sanitizeInboundServerConfig ( value : unknown ) : InboundServerConfig | undefined {
59+ if ( ! value || typeof value !== "object" || Array . isArray ( value ) ) return
60+ const record = value as Record < string , unknown >
5261 return {
53- enabled : typeof enabled === "boolean" ? enabled : false ,
54- username : typeof username === "string" ? username : "" ,
55- password : typeof password === "string" ? password : "" ,
56- port : typeof port === "number" && Number . isInteger ( port ) && port > 0 && port <= 65535 ? port : null ,
62+ enabled : typeof record . enabled === "boolean" ? record . enabled : false ,
63+ username : typeof record . username === "string" ? record . username : "" ,
64+ password : typeof record . password === "string" ? record . password : "" ,
65+ port :
66+ typeof record . port === "number" && Number . isInteger ( record . port ) && record . port > 0 && record . port <= 65535
67+ ? record . port
68+ : null ,
5769 }
5870}
5971
60- export function getInboundRuntimeServerConfig ( ) : InboundServerConfig {
61- return runtimeInboundServerConfig ?? { enabled : false , username : "opencode" , password : "" , port : null }
72+ function parseConfigText ( text : string ) {
73+ try {
74+ return JSON . parse ( text ) as Record < string , unknown >
75+ } catch { }
76+ try {
77+ return JSON . parse (
78+ text
79+ . replace ( / \/ \* [ \s \S ] * ?\* \/ / g, "" )
80+ . replace ( / ^ \s * \/ \/ .* $ / gm, "" )
81+ . replace ( / , \s * ( [ } \] ] ) / g, "$1" ) ,
82+ ) as Record < string , unknown >
83+ } catch {
84+ return
85+ }
6286}
6387
64- export function setInboundServerConfig ( config : InboundServerConfig ) {
65- getStore ( ) . set ( INBOUND_ENABLED_KEY , config . enabled )
66- getStore ( ) . set ( INBOUND_USERNAME_KEY , config . username )
67- getStore ( ) . set ( INBOUND_PASSWORD_KEY , config . password )
68- if ( config . port === null ) {
69- getStore ( ) . delete ( INBOUND_PORT_KEY )
88+ function readInboundServerConfigFromConfigFile ( ) {
89+ try {
90+ const value = parseConfigText ( readFileSync ( configFile ( ) , "utf8" ) )
91+ if ( ! value ) return
92+ return sanitizeInboundServerConfig ( value . localServer )
93+ } catch {
7094 return
7195 }
96+ }
97+
98+ function writableInboundServerConfig ( config : InboundServerConfig ) {
99+ return {
100+ enabled : config . enabled ,
101+ ...( config . port !== null ? { port : config . port } : { } ) ,
102+ ...( config . username ? { username : config . username } : { } ) ,
103+ ...( config . password ? { password : config . password } : { } ) ,
104+ }
105+ }
106+
107+ function writeInboundServerConfigToConfigFile ( config : InboundServerConfig ) {
108+ const file = configFile ( )
109+ mkdirSync ( path . dirname ( file ) , { recursive : true } )
110+ const next = writableInboundServerConfig ( config )
111+ const current = existsSync ( file ) ? ( parseConfigText ( readFileSync ( file , "utf8" ) ) ?? { } ) : { }
112+ writeFileSync ( file , `${ JSON . stringify ( { ...current , localServer : next } , null , 2 ) } \n` )
113+ }
72114
73- getStore ( ) . set ( INBOUND_PORT_KEY , config . port )
115+ export function getInboundServerConfig ( ) : InboundServerConfig {
116+ return readInboundServerConfigFromConfigFile ( ) ?? emptyInboundServerConfig
117+ }
118+
119+ export function getInboundRuntimeServerConfig ( ) : InboundServerConfig {
120+ return runtimeInboundServerConfig ?? emptyInboundServerConfig
121+ }
122+
123+ export function setInboundServerConfig ( config : InboundServerConfig ) {
124+ writeInboundServerConfigToConfigFile ( config )
74125}
75126
76127export async function spawnLocalServer ( hostname : string , port : number , username : string , password : string ) {
0 commit comments