@agent-webui/ai-desk-daemon 1.0.61-beta5 → 1.0.61-beta7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -12,7 +12,12 @@ const { start, stop, restart, status } = require('../lib/daemon-manager');
12
12
  const { getLogPath } = require('../lib/platform');
13
13
  const { VERSION } = require('../lib/platform');
14
14
  const { getPort } = require('../lib/config');
15
- const { reportLifecycle } = require('../lib/daemon-registry');
15
+ const {
16
+ configureRegistryUrl,
17
+ getRegistryConfig,
18
+ reportLifecycle,
19
+ syncRegistryConfigToDaemonConfig,
20
+ } = require('../lib/daemon-registry');
16
21
  const { upgradePackage } = require('../lib/self-upgrade');
17
22
 
18
23
  function wait(ms) {
@@ -141,11 +146,63 @@ async function reportRegistryLifecycle(event) {
141
146
  }
142
147
  }
143
148
 
149
+ function syncRegistryConfigForDaemonStart() {
150
+ try {
151
+ syncRegistryConfigToDaemonConfig();
152
+ } catch (error) {
153
+ console.warn(chalk.yellow(`[registry] Failed to sync daemon registry config: ${error.message || error}`));
154
+ }
155
+ }
156
+
144
157
  program
145
158
  .name('aidesk')
146
159
  .description('AI Desk Daemon - CLI tool for managing the AI Desk daemon service')
147
160
  .version(VERSION);
148
161
 
162
+ const registryCommand = program
163
+ .command('registry')
164
+ .description('Manage shared runtime registry backend settings');
165
+
166
+ registryCommand
167
+ .command('set-url <url>')
168
+ .description('Persist the AI Desk backend URL used for daemon registry reporting')
169
+ .option('--insecure-skip-tls-verify', 'Skip TLS certificate verification for this registry URL')
170
+ .option('--strict-tls', 'Require strict TLS certificate verification for this registry URL')
171
+ .action((url, options) => {
172
+ try {
173
+ if (options.insecureSkipTlsVerify && options.strictTls) {
174
+ throw new Error('Use either --insecure-skip-tls-verify or --strict-tls, not both');
175
+ }
176
+ const tlsInsecureSkipVerify = options.insecureSkipTlsVerify
177
+ ? true
178
+ : (options.strictTls ? false : undefined);
179
+ const config = configureRegistryUrl(url, { tlsInsecureSkipVerify });
180
+ console.log(chalk.green('✓ Registry URL updated'));
181
+ console.log(`API base URL: ${config.apiBaseUrl}`);
182
+ console.log(`TLS verification: ${config.tlsInsecureSkipVerify ? 'skip for this URL' : 'strict'}`);
183
+ console.log(chalk.cyan('Restart the daemon for the new registry URL to be used by background heartbeats.'));
184
+ } catch (error) {
185
+ console.error(chalk.red('Failed to update registry URL:'), error.message);
186
+ process.exit(1);
187
+ }
188
+ });
189
+
190
+ registryCommand
191
+ .command('status')
192
+ .description('Show the effective daemon registry backend settings')
193
+ .action(() => {
194
+ const config = getRegistryConfig();
195
+ if (!config) {
196
+ console.log(chalk.yellow('Registry reporting is disabled'));
197
+ return;
198
+ }
199
+ console.log(`API base URL: ${config.apiBaseUrl}`);
200
+ console.log(`RC account ID: ${config.rcAccountId || '(empty)'}`);
201
+ console.log(`RC extension ID: ${config.rcExtensionId || '(empty)'}`);
202
+ console.log(`RC username: ${config.rcUsername || '(empty)'}`);
203
+ console.log(`TLS verification: ${config.tlsInsecureSkipVerify ? 'skip for this URL' : 'strict'}`);
204
+ });
205
+
149
206
  // Start command
150
207
  program
151
208
  .command('start')
@@ -158,6 +215,7 @@ program
158
215
  try {
159
216
  const mode = resolveRequestedMode(options) || 'native';
160
217
  const modeResult = configureRequestedMode(mode);
218
+ syncRegistryConfigForDaemonStart();
161
219
  start();
162
220
  await reportRegistryLifecycle('start');
163
221
  if (mode === 'cli-anything' && modeResult?.runtimeInfo?.cliAnythingPath) {
@@ -257,6 +315,7 @@ program
257
315
  try {
258
316
  const mode = resolveRequestedMode(options);
259
317
  const modeResult = configureRequestedMode(mode);
318
+ syncRegistryConfigForDaemonStart();
260
319
  restart();
261
320
  await reportRegistryLifecycle('restart');
262
321
  if (mode === 'cli-anything' && modeResult?.runtimeInfo?.cliAnythingPath) {
@@ -25,16 +25,44 @@ function parseTruthyEnv(value) {
25
25
  return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
26
26
  }
27
27
 
28
+ function isPrivateRegistryHost(hostname) {
29
+ const host = trim(hostname).toLowerCase();
30
+ return (
31
+ host === 'localhost' ||
32
+ host === '127.0.0.1' ||
33
+ host.endsWith('.local') ||
34
+ /^10\./.test(host) ||
35
+ /^192\.168\./.test(host) ||
36
+ /^172\.(1[6-9]|2\d|3[0-1])\./.test(host)
37
+ );
38
+ }
39
+
40
+ function inferTLSInsecureSkipVerify(apiBaseUrl) {
41
+ try {
42
+ const url = new URL(normalizeApiBaseUrl(apiBaseUrl));
43
+ if (url.protocol !== 'https:') return false;
44
+ if (url.hostname === 'desk.int.rclabenv.com') return false;
45
+ return isPrivateRegistryHost(url.hostname);
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+
28
51
  function readRegistrySessionIdentity(homeDir = os.homedir()) {
29
52
  const sessionPath = path.join(homeDir, '.aidesktop', 'session.json');
30
53
  try {
31
54
  if (!fs.existsSync(sessionPath)) return null;
32
55
  const session = JSON.parse(fs.readFileSync(sessionPath, 'utf8'));
33
56
  if (!session || typeof session !== 'object') return null;
57
+ const apiBaseUrl = normalizeApiBaseUrl(session.api_base_url || session.apiBaseUrl);
34
58
  const rcAccountId = trim(session.account_id || session.accountId);
35
59
  const rcExtensionId = trim(session.extension_id || session.extensionId);
36
- if (!rcAccountId || !rcExtensionId) return null;
37
- return { rcAccountId, rcExtensionId };
60
+ const rcUsername = trim(session.rc_username || session.rcUsername);
61
+ const tlsInsecureSkipVerify = Boolean(
62
+ session.tls_insecure_skip_verify || session.tlsInsecureSkipVerify,
63
+ );
64
+ if (!apiBaseUrl && !rcAccountId && !rcExtensionId && !rcUsername) return null;
65
+ return { apiBaseUrl, rcAccountId, rcExtensionId, rcUsername, tlsInsecureSkipVerify };
38
66
  } catch {
39
67
  return null;
40
68
  }
@@ -55,12 +83,12 @@ function readRegistryConfigFromDaemonConfig(homeDir = os.homedir()) {
55
83
  sessionIdentity?.rcAccountId || '';
56
84
  const rcExtensionId = trim(registry.rc_extension_id || registry.rcExtensionId) ||
57
85
  sessionIdentity?.rcExtensionId || '';
58
- if (!apiBaseUrl || !rcAccountId || !rcExtensionId) return null;
86
+ if (!apiBaseUrl) return null;
59
87
  return {
60
88
  apiBaseUrl,
61
89
  rcAccountId,
62
90
  rcExtensionId,
63
- rcUsername: trim(registry.rc_username || registry.rcUsername),
91
+ rcUsername: trim(registry.rc_username || registry.rcUsername) || sessionIdentity?.rcUsername || '',
64
92
  tlsInsecureSkipVerify: Boolean(registry.tls_insecure_skip_verify || registry.tlsInsecureSkipVerify),
65
93
  };
66
94
  } catch {
@@ -71,32 +99,121 @@ function readRegistryConfigFromDaemonConfig(homeDir = os.homedir()) {
71
99
  function getRegistryConfig(env = process.env, homeDir = os.homedir()) {
72
100
  const daemonConfig = readRegistryConfigFromDaemonConfig(homeDir);
73
101
  const sessionIdentity = readRegistrySessionIdentity(homeDir);
74
- const apiBaseUrl = normalizeApiBaseUrl(
102
+ const envApiBaseUrl = normalizeApiBaseUrl(
75
103
  env.AI_DESK_API_BASE_URL || env.AI_DESK_BACKEND_API_URL || env.AI_DESK_BACKEND_URL,
76
- ) || daemonConfig?.apiBaseUrl || normalizeApiBaseUrl(DEFAULT_REGISTRY_API_BASE_URL);
104
+ );
105
+ const apiBaseUrl = normalizeApiBaseUrl(
106
+ envApiBaseUrl ||
107
+ daemonConfig?.apiBaseUrl ||
108
+ sessionIdentity?.apiBaseUrl ||
109
+ DEFAULT_REGISTRY_API_BASE_URL,
110
+ );
77
111
  const rcAccountId = trim(env.AI_DESK_RC_ACCOUNT_ID || env.rcAccountId) ||
78
- daemonConfig?.rcAccountId ||
79
112
  sessionIdentity?.rcAccountId ||
113
+ daemonConfig?.rcAccountId ||
80
114
  '';
81
115
  const rcExtensionId = trim(env.AI_DESK_RC_EXTENSION_ID || env.rcExtensionId) ||
82
- daemonConfig?.rcExtensionId ||
83
116
  sessionIdentity?.rcExtensionId ||
117
+ daemonConfig?.rcExtensionId ||
84
118
  '';
85
- if (apiBaseUrl && rcAccountId && rcExtensionId) {
119
+ if (apiBaseUrl) {
86
120
  const tlsEnvValue = parseTruthyEnv(env.AI_DESK_TLS_INSECURE_SKIP_VERIFY);
87
121
  return {
88
122
  apiBaseUrl,
89
123
  rcAccountId,
90
124
  rcExtensionId,
91
- rcUsername: trim(env.AI_DESK_RC_USERNAME || env.rcUsername) || daemonConfig?.rcUsername || '',
125
+ rcUsername: trim(env.AI_DESK_RC_USERNAME || env.rcUsername) ||
126
+ sessionIdentity?.rcUsername ||
127
+ daemonConfig?.rcUsername ||
128
+ '',
92
129
  tlsInsecureSkipVerify: tlsEnvValue === null
93
- ? Boolean(daemonConfig?.tlsInsecureSkipVerify)
130
+ ? Boolean(daemonConfig?.tlsInsecureSkipVerify ?? sessionIdentity?.tlsInsecureSkipVerify ?? inferTLSInsecureSkipVerify(apiBaseUrl))
94
131
  : tlsEnvValue,
95
132
  };
96
133
  }
97
134
  return null;
98
135
  }
99
136
 
137
+ function daemonConfigPath(homeDir = os.homedir()) {
138
+ return path.join(homeDir, '.aidesktop', 'daemon-config.json');
139
+ }
140
+
141
+ function syncRegistryConfigToDaemonConfig(env = process.env, homeDir = os.homedir()) {
142
+ const config = getRegistryConfig(env, homeDir);
143
+ if (!config) return null;
144
+
145
+ const configPath = daemonConfigPath(homeDir);
146
+ let daemonConfig = {};
147
+ try {
148
+ if (fs.existsSync(configPath)) {
149
+ const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
150
+ if (parsed && typeof parsed === 'object') daemonConfig = parsed;
151
+ }
152
+ } catch {
153
+ daemonConfig = {};
154
+ }
155
+
156
+ daemonConfig.registry = {
157
+ ...(daemonConfig.registry && typeof daemonConfig.registry === 'object' ? daemonConfig.registry : {}),
158
+ api_base_url: config.apiBaseUrl,
159
+ rc_account_id: config.rcAccountId,
160
+ rc_extension_id: config.rcExtensionId,
161
+ rc_username: config.rcUsername,
162
+ tls_insecure_skip_verify: Boolean(config.tlsInsecureSkipVerify),
163
+ };
164
+
165
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
166
+ fs.writeFileSync(configPath, `${JSON.stringify(daemonConfig, null, 2)}\n`, 'utf8');
167
+ return config;
168
+ }
169
+
170
+ function configureRegistryUrl(apiBaseUrl, options = {}) {
171
+ const homeDir = options.homeDir || os.homedir();
172
+ const normalizedApiBaseUrl = normalizeApiBaseUrl(apiBaseUrl);
173
+ if (!normalizedApiBaseUrl) {
174
+ throw new Error('Registry URL is required');
175
+ }
176
+
177
+ let parsedUrl;
178
+ try {
179
+ parsedUrl = new URL(normalizedApiBaseUrl);
180
+ } catch {
181
+ throw new Error(`Invalid registry URL: ${apiBaseUrl}`);
182
+ }
183
+ if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
184
+ throw new Error(`Registry URL must use http or https: ${apiBaseUrl}`);
185
+ }
186
+
187
+ const configPath = daemonConfigPath(homeDir);
188
+ let daemonConfig = {};
189
+ try {
190
+ if (fs.existsSync(configPath)) {
191
+ const parsedConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
192
+ if (parsedConfig && typeof parsedConfig === 'object') daemonConfig = parsedConfig;
193
+ }
194
+ } catch {
195
+ daemonConfig = {};
196
+ }
197
+
198
+ const tlsInsecureSkipVerify = typeof options.tlsInsecureSkipVerify === 'boolean'
199
+ ? options.tlsInsecureSkipVerify
200
+ : inferTLSInsecureSkipVerify(normalizedApiBaseUrl);
201
+
202
+ daemonConfig.registry = {
203
+ ...(daemonConfig.registry && typeof daemonConfig.registry === 'object' ? daemonConfig.registry : {}),
204
+ api_base_url: normalizedApiBaseUrl,
205
+ tls_insecure_skip_verify: tlsInsecureSkipVerify,
206
+ };
207
+
208
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
209
+ fs.writeFileSync(configPath, `${JSON.stringify(daemonConfig, null, 2)}\n`, 'utf8');
210
+
211
+ return {
212
+ apiBaseUrl: normalizedApiBaseUrl,
213
+ tlsInsecureSkipVerify,
214
+ };
215
+ }
216
+
100
217
  function registryStatePaths(homeDir = os.homedir()) {
101
218
  const root = path.join(homeDir, '.aidesktop');
102
219
  return {
@@ -206,8 +323,8 @@ function postJSON(urlString, payload, config) {
206
323
  headers: {
207
324
  'Content-Type': 'application/json',
208
325
  'Content-Length': Buffer.byteLength(body),
209
- rcAccountId: config.rcAccountId,
210
- rcExtensionId: config.rcExtensionId,
326
+ ...(config.rcAccountId ? { rcAccountId: config.rcAccountId } : {}),
327
+ ...(config.rcExtensionId ? { rcExtensionId: config.rcExtensionId } : {}),
211
328
  ...(config.rcUsername ? { rcUsername: config.rcUsername } : {}),
212
329
  },
213
330
  timeout: 3000,
@@ -259,8 +376,11 @@ async function reportLifecycle(event, { port, version, seq = 1 } = {}) {
259
376
 
260
377
  module.exports = {
261
378
  buildLifecyclePayload,
379
+ configureRegistryUrl,
262
380
  DEFAULT_REGISTRY_API_BASE_URL,
381
+ inferTLSInsecureSkipVerify,
263
382
  getRegistryConfig,
383
+ syncRegistryConfigToDaemonConfig,
264
384
  readRegistrySessionIdentity,
265
385
  readRegistryConfigFromDaemonConfig,
266
386
  registryStatePaths,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-webui/ai-desk-daemon",
3
- "version": "1.0.61-beta5",
3
+ "version": "1.0.61-beta7",
4
4
  "description": "AI Desk Daemon - CLI tool for managing the AI Desk daemon service",
5
5
  "workspaces": [
6
6
  "packages/*"
@@ -39,16 +39,16 @@
39
39
  "chalk": "^4.1.2"
40
40
  },
41
41
  "optionalDependencies": {
42
- "@agent-webui/ai-desk-daemon-darwin-arm64": "1.0.61-beta5",
43
- "@agent-webui/ai-desk-daemon-darwin-x64": "1.0.61-beta5",
44
- "@agent-webui/ai-desk-daemon-linux-arm64": "1.0.61-beta5",
45
- "@agent-webui/ai-desk-daemon-linux-x64": "1.0.61-beta5",
46
- "@agent-webui/ai-desk-daemon-win32-x64": "1.0.61-beta5",
47
- "@agent-webui/ai-desk-python-darwin-arm64": "1.0.61-beta5",
48
- "@agent-webui/ai-desk-python-darwin-x64": "1.0.61-beta5",
49
- "@agent-webui/ai-desk-python-linux-arm64": "1.0.61-beta5",
50
- "@agent-webui/ai-desk-python-linux-x64": "1.0.61-beta5",
51
- "@agent-webui/ai-desk-python-win32-x64": "1.0.61-beta5"
42
+ "@agent-webui/ai-desk-daemon-darwin-arm64": "1.0.61-beta7",
43
+ "@agent-webui/ai-desk-daemon-darwin-x64": "1.0.61-beta7",
44
+ "@agent-webui/ai-desk-daemon-linux-arm64": "1.0.61-beta7",
45
+ "@agent-webui/ai-desk-daemon-linux-x64": "1.0.61-beta7",
46
+ "@agent-webui/ai-desk-daemon-win32-x64": "1.0.61-beta7",
47
+ "@agent-webui/ai-desk-python-darwin-arm64": "1.0.61-beta7",
48
+ "@agent-webui/ai-desk-python-darwin-x64": "1.0.61-beta7",
49
+ "@agent-webui/ai-desk-python-linux-arm64": "1.0.61-beta7",
50
+ "@agent-webui/ai-desk-python-linux-x64": "1.0.61-beta7",
51
+ "@agent-webui/ai-desk-python-win32-x64": "1.0.61-beta7"
52
52
  },
53
53
  "repository": {
54
54
  "type": "git",