@agentsdock/server 1.0.10-beta.1 → 1.0.10-beta.6

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/README.md CHANGED
@@ -20,6 +20,12 @@ the same prerequisites as the source installer: a trusted `uv`, Linux with a
20
20
  working user systemd session or Apple silicon macOS with a GUI launchd session,
21
21
  and the desired provider CLIs. It runs as the current user without `sudo`.
22
22
 
23
+ Installation and recovery exclude temporary npm lifecycle `.bin` paths, npm
24
+ `_npx` cache paths and npm node-gyp shims from the inherited PATH. Stable absolute
25
+ user/provider/Node locations remain ordered; empty and relative entries are
26
+ excluded. This lexical filter is not executable or symlink authorization and
27
+ does not repair the persisted PATH of an existing installation.
28
+
23
29
  The npm entrypoint only permits a fresh default installation. It refuses an
24
30
  existing managed installation, legacy service, or custom installation roots;
25
31
  changing directories does not create a second service. Use AgentsDock's managed
package/npm/cli.cjs CHANGED
@@ -23,6 +23,27 @@ No installation hooks run when this npm package is installed.
23
23
  `
24
24
  const ROOT_SELECTORS = ['AGENTS_SERVER_INSTALL_DIR', 'AGENTS_SERVER_CONFIG_DIR', 'AGENTS_SERVER_STATE_DIR', 'AGENTSDOCK_STATE_DIR', 'ZENITHBOT_AGENT_DIR', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME']
25
25
 
26
+ function persistentPath(value) {
27
+ if (value === undefined) return undefined
28
+ // npm adds package/ancestor .bin directories and node-gyp shims to lifecycle
29
+ // PATH; npx additionally exposes its disposable cache. These must not become
30
+ // dependencies of a long-lived native service. Keep stable absolute user,
31
+ // provider and Node paths in order; do not trust npm env as an "original PATH".
32
+ // This is a lexical persistence filter, not executable/symlink authorization.
33
+ const entries = new Set()
34
+ for (const entry of String(value).split(path.delimiter)) {
35
+ if (!path.isAbsolute(entry) || /[\x00-\x1f\x7f]/.test(entry)) continue
36
+ const normalized = path.normalize(entry).replace(/\/$/, '') || '/'
37
+ if (/\/node_modules\/\.bin$/.test(normalized) ||
38
+ /\/_npx\/[^/]+(?:\/|$)/.test(normalized) ||
39
+ /\/node_modules\/(?:npm\/bin|@npmcli\/run-script\/lib)\/node-gyp-bin$/.test(normalized)) continue
40
+ // Do not rewrite retained paths across symlink/.. boundaries: lexical
41
+ // normalization is only for recognizing npm entries, not resolving paths.
42
+ entries.add(entry)
43
+ }
44
+ return entries.size ? [...entries].join(path.delimiter) : '/usr/bin:/bin:/usr/sbin:/sbin'
45
+ }
46
+
26
47
  function parse(argv) {
27
48
  const [command = 'help', ...args] = argv
28
49
  if (['help', '--help', '-h', '--version'].includes(command)) {
@@ -66,7 +87,8 @@ function ensureFreshInstall(context) {
66
87
  if (context.uid === 0) throw new Error('Run as the user who will own the server, without sudo.')
67
88
  if (!['darwin', 'linux'].includes(context.platform)) throw new Error('Server installation requires Linux or Apple silicon macOS.')
68
89
  for (const name of ROOT_SELECTORS) if (context.env[name]) throw new Error(`Custom installation selector ${name} is unsupported by the fresh-install CLI. Use the existing server's managed updater.`)
69
- const existingInstall = () => new Error('An existing server installation or state was found. Use AgentsDock or agentsdock-server update; no installer was started.')
90
+ const existingInstall = (message = 'An existing server installation or state was found. Use AgentsDock or agentsdock-server update; no installer was started.') =>
91
+ Object.assign(new Error(message), { code: 'AGENTSDOCK_EXISTING_INSTALLATION' })
70
92
  const safeDirectory = info => info.isDirectory() && !info.isSymbolicLink() && info.uid === context.uid && (info.mode & 0o022) === 0 && (info.mode & 0o500) === 0o500
71
93
  const allowEmptyScaffold = (relative, child) => {
72
94
  const root = path.join(context.home, relative)
@@ -100,11 +122,14 @@ function ensureFreshInstall(context) {
100
122
  }
101
123
  if (context.platform === 'darwin') {
102
124
  const check = context.spawn('/bin/launchctl', ['print', `gui/${context.uid}/com.agentsdock.server`], { encoding: 'utf8', timeout: 10000 })
103
- if (check.status === 0) throw new Error('An AgentsServer service already exists. Use its managed updater.')
125
+ if (check.status === 0) throw existingInstall('An AgentsServer service already exists. Use its managed updater.')
104
126
  if (check.error || !/could not find service|service.*not found/i.test(`${check.stdout || ''} ${check.stderr || ''}`)) throw new Error('Could not verify the user service is absent. No installer was started.')
105
127
  } else {
106
128
  for (const unit of ['agents-server.service', 'zenithbot-agent.service']) {
107
129
  const check = context.spawn('systemctl', ['--user', 'show', unit, '--property=LoadState', '--value'], { encoding: 'utf8', timeout: 10000 })
130
+ if (!check.error && check.status === 0 && String(check.stdout).trim() && String(check.stdout).trim() !== 'not-found') {
131
+ throw existingInstall('An existing user service was found. Use its managed updater.')
132
+ }
108
133
  if (check.error || String(check.stdout).trim() !== 'not-found') throw new Error('An existing user service was found or its absence could not be verified. Use its managed updater.')
109
134
  }
110
135
  }
@@ -232,6 +257,7 @@ async function run(argv, overrides = {}) {
232
257
  const payload = path.join(context.packageRoot, 'server')
233
258
  const payloadVersion = readRegular(path.join(payload, 'VERSION'), 200).toString('utf8').trim()
234
259
  if (payloadVersion !== metadata.version) throw new Error('Package and server payload versions do not match.')
260
+ if (Object.hasOwn(context.env, 'PATH')) context.env = { ...context.env, PATH: persistentPath(context.env.PATH) }
235
261
  if (command === 'recover') {
236
262
  const recovery = recoveryContext(context)
237
263
  if (!recovery) { context.print('No unfinished default server migration was found. No changes were made.'); return 0 }
@@ -286,4 +312,4 @@ async function run(argv, overrides = {}) {
286
312
  }
287
313
 
288
314
  if (require.main === module) run(process.argv.slice(2)).then(code => { process.exitCode = code }).catch(error => { process.stderr.write(`agentsdock-server: ${error.message}\n`); process.exitCode = 1 })
289
- module.exports = { parse, readRegular, ensureFreshInstall, recoveryContext, validateOrigin, run }
315
+ module.exports = { parse, readRegular, ensureFreshInstall, recoveryContext, validateOrigin, persistentPath, run }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentsdock/server",
3
- "version": "1.0.10-beta.1",
3
+ "version": "1.0.10-beta.6",
4
4
  "description": "AgentsDock's self-hosted Python server and explicit installation CLI",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/server/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.10-beta.1
1
+ 1.0.10-beta.6